Home / Projects / FireworkVisualiser

Game Design

Firework Visualiser

Interactive browser-based music visualizer that launches synchronized fireworks in response to the beat and intensity of any song.

Role Developer / Researcher
Duration 1 Week
Stack Unity / C#
Focus Audio
Related Project

KitKat Advertisement

May 2024

A dive into learning C# and finding the most efficient way to calculate squart roots.

Video Editing Experimental Blender
View project

Project Overview

What this project is

This project is a Unity-based audio-reactive fireworks system that analyses a music track and automatically choreographs a synchronized fireworks show. The system processes the audio file before playback, detecting rhythmic beats and vocal accents using signal-analysis techniques such as energy envelopes, onset detection, and spectral flux. Each detected event is assigned a launch time and flight duration so that rockets explode precisely on the musical moment they correspond to.

The system differentiates between different musical features: stronger beats trigger larger fireworks, while vocal accents generate additional visual highlights. Rockets are selected from configurable pools, allowing different firework types to represent different musical intensities. All events are scheduled ahead of time so that the fireworks appear tightly synchronized with the audio.

The result is an automated music visualizer where a fireworks display dynamically responds to the structure, rhythm, and energy of the song, creating a coordinated audio-visual performance.

Interactive Demo

Playable browser build

This demo runs directly in your browser using a Unity WebGL build. Use your mouse and keyboard to interact with the simulation.

Fullscreen

Notes

What is the interactive demo?

This demo showcases a browser-based fireworks visualiser that synchronises a fireworks display with a music track. The system analyses an audio file to detect rhythmic beats and vocal accents, then schedules fireworks to launch so that they explode exactly on those moments in the music.

Different types of fireworks are triggered depending on the strength of the detected beat, allowing stronger musical moments to produce larger or more dramatic explosions. Additional visual accents can also appear during vocal peaks to enhance the sense of rhythm and energy in the display.

The purpose of the demo is to demonstrate how audio analysis can be used to automatically generate a choreographed visual show, turning any music track into a synchronised fireworks performance.

Code


    private void AnalyzeAndBuildShow()
    {
        beatSchedules.Clear();
        smallRocketCursor = 0;
        bigRocketCursor = 0;
        vocalRocketCursor = 0;

        int channels = musicClip.channels;
        int totalSamples = musicClip.samples;
        int totalValues = totalSamples * channels;

        float[] raw = new float[totalValues];
        musicClip.GetData(raw, 0);

        float[] mono = ConvertToMono(raw, channels);

        // Main beat lane 
        float[] energyEnvelope = BuildEnergyEnvelope(mono, windowSize, hopSize);
        float[] onsetEnvelope = BuildOnsetEnvelope(energyEnvelope);
        float[] smoothedOnset = SmoothArray(onsetEnvelope, 2);

        List mainCandidates = DetectCandidates(
            smoothedOnset,
            hopSize,
            musicClip.frequency,
            minBeatSeparation,
            thresholdMultiplier,
            localAverageRadius);

        detectedBeatCount = mainCandidates.Count;
        NormalizeCandidateStrengths(mainCandidates);
        BuildSchedulesFromMainCandidates(mainCandidates);

        // Vocal lane
        detectedVocalCount = 0;
        scheduledVocalCount = 0;

        if (enableVocalAccents)
        {
            float[] vocalFlux = BuildVocalSpectralFluxEnvelope(
                mono,
                windowSize,
                hopSize,
                musicClip.frequency,
                vocalMinFrequency,
                vocalMaxFrequency,
                useVocalBandNormalization);

            float[] smoothedVocalFlux = SmoothArray(vocalFlux, 1);

            List vocalCandidates = DetectCandidates(
                smoothedVocalFlux,
                hopSize,
                musicClip.frequency,
                minVocalSeparation,
                vocalThresholdMultiplier,
                vocalAverageRadius);

            detectedVocalCount = vocalCandidates.Count;
            NormalizeCandidateStrengths(vocalCandidates);
            BuildSchedulesFromVocalCandidates(vocalCandidates);
        }

        beatSchedules.Sort((a, b) => a.launchTime.CompareTo(b.launchTime));

        scheduledBeatCount = 0;
        scheduledVocalCount = 0;

        for (int i = 0; i < beatSchedules.Count; i++)
        {
            if (beatSchedules[i].eventType == EventType.Beat)
                scheduledBeatCount++;
            else if (beatSchedules[i].eventType == EventType.VocalAccent)
                scheduledVocalCount++;
        }

        if (logBeatCount)
        {
            Debug.Log(
                $"Detected beats: {detectedBeatCount}, scheduled beats: {scheduledBeatCount}, " +
                $"detected vocals: {detectedVocalCount}, scheduled vocals: {scheduledVocalCount} for {musicClip.name}");
        }
    }
              

Technical Notes

Challenges and what I learned

Talk about the hardest parts, what changed during development, what trade-offs you made, and what you would improve next.