Three effects, one envelope: how the bass signal became the nervous system of this visualiser

The opacity expression for the city’s neon bloom reads min(1, 0.14 + kickEnv*0.7 + bass*0.22). Three terms: a floor, a kick envelope contribution, a continuous bass contribution. When that line went in, the particle system was already running and the spring panorama was already behind it. The bloom was one more layer.

Two days later, the bass envelope was driving three independent visual surfaces. That wasn’t the plan coming in. It was what the track pulled out.

The track

Before the architecture makes sense, the audio does. The visualiser runs on public/tasogare.wav: TASOGARE, CAPTAIN RANDOM, 3:16, 48kHz 24-bit stereo. Not a placeholder. A real track with a specific kick pattern that lands hard and regularly on low frequencies.

That last part matters. The reason the bass envelope ends up as a useful central signal is that TASOGARE has a kick that is rhythmically legible and tonally consistent. Drive three visual effects off an inconsistent or weak bass signal and you get noise. Drive them off this one and you get a visualiser that reads as intentional.

This architecture is correct for this track. Generalising it is out of scope.

The Claude Design foundation

The particle system, beat flash, waveform, now-playing card, and RGB-split came through a Claude Design handover, ingested live in-session via the DesignSync MCP tool (get_project / list_files / get_file). Those additions were implemented against the real track, not a placeholder. That choice meant the bass-reactivity work that followed could tune immediately rather than requiring a second pass once real audio arrived.

The now-playing card landed in the same session: TRACK_TITLE set to TASOGARE, TRACK_SUB to CAPTAIN RANDOM. Previous placeholder names were dropped.

The bass routing described below built on top of that foundation.

Neon bloom

First of the three bass-driven effects.

The bloom sits in the background behind the now-playing card, representing city neon. Opacity is min(1, 0.14 + kickEnv*0.7 + bass*0.22).

The 0.14 floor means the bloom is always visible, dim but present. The kick envelope (kickEnv) is the dominant driver: at a hard transient it pushes the total toward 0.84. The continuous bass term (bass*0.22) layers underneath, adding a slower pulse between kicks so the bloom breathes rather than just flashing.

Gradient direction is pink to gold to transparent. The city visibly flares on each low-end hit and settles back between them.

Out of scope: dynamic colour variation. The palette is fixed. What changes is only the opacity.

Particle life-arc

Onset particles spawn on detected transients. The system was reworked from a simple emission into a life-arc.

Energy is now an explicit per-particle value, initialised at maximum on spawn and decaying each frame: energy = 0.9^age. The coefficient scales a perpendicular wobble: sin(age · freq + phase) · amp · energy. At spawn, energy is 1.0. The wobble is wide and fast. As the particle ages, the coefficient drops toward zero. Amplitude narrows. The trajectory tightens.

The bass envelope contributes to amp. A particle spawned on a kick launches into a harder wobble than one spawned in a gap. During low-end hits, newly born particles throw sideways before settling. In the spaces between, the motion is cleaner.

Lifespan is 2× the original value. The arc needs time to read. Too short and the wobble decays before completing a full cycle.

The grow-over-life element runs independently: particle radius scales from a small spawn value up to a peak, then back toward zero at death. That shapes visual weight over the arc without touching the wobble or the bass input. The two systems, wobble-over-energy and size-over-life, compose without competing.

Temporal portal

The third effect is a radial window cut through the spring panorama.

A second image sits as the rear layer: public/bg-winter.jpg, 3.4 MB, 3904×1088. Same park, same camera angle. Snow, icicles, kids with snowballs. The front (spring) layer carries a radial gradient mask; the winter scene shows through wherever that mask is transparent.

Bass drives the mask radius. On a hard hit the portal widens and winter pushes forward. Between hits it contracts; spring reasserts. The two panoramas are static. All movement is the mask, driven by the same envelope that runs the bloom and the particle wobble.

The brief called it a “peek into a different time.” The low end controls how far open that view is at any given moment.

Three surfaces, one signal

The convergence wasn’t designed in advance. The bloom came first and established that the bass envelope was being extracted and available. The particle wobble needed a driven energy source; the same signal was there. The portal mask needed a value that kept it rhythmically in sync with the other effects; using an independent signal would have decoupled the three layers.

Each decision was locally sensible. Collectively they produced a unified routing pattern.

One extraction controls the low-end feel of the entire visualiser. Change how bass is smoothed and all three surfaces respond. Centralisation suits this track. The kick is the rhythmic spine.

The trade-off is explicit. The three effects share one envelope curve. Making the bloom respond differently to bass than the portal requires splitting the signal into two separate paths. That capability is absent. It is absent by choice, not by oversight. The visualiser is for this track.

Signal routing as architecture

In practice the chain is shallow. The audio analyser runs on each frame. Bass is extracted as a single normalised value in the 0–1 range. The kick envelope detects transients and holds a decaying peak. Both feed three rendering functions.

No shared mutable state. Each visual layer reads the signal and computes its own output independently. The bloom cannot influence the portal, and the particles cannot modulate the bloom. Coupling is read-only and unidirectional.

That keeps the three systems individually debuggable. If the portal is misbehaving, the problem is in the signal or in the portal’s own mask computation. Not in shared state elsewhere.

One sentence

The bass envelope is extracted once. Bloom opacity, particle wobble, and portal radius each read it. Change the extraction and all three move.

All writing