Game Design
Firework Visualiser
Interactive browser-based music visualizer that launches synchronized fireworks in response to the beat and intensity of any song.
KitKat Advertisement
May 2024A dive into learning C# and finding the most efficient way to calculate squart roots.
View projectProject 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.
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.
References