Relay light lab

A rebuild of the light that crosses the “relay” card on opentunnel.xyz. Every control below changes the real effect live. Pause it, slow it down, switch layers off one at a time, and read the notes under the bench.

1.0 is the softer first version.
relay

playback

Slows the whole scene, so you can watch a single front cross.
How long one message takes to cross the card.

layers (switch off to see what each adds)

tuning

the memory strip: 256 timers, one per column of the card

left edge of the cardright edge
Hover or drag across the strip to read one column.

What you are looking at

The card is ordinary HTML: a black box, two borders and the word “relay”. Sitting exactly on top of it is a transparent <canvas>. Each frame, a small program on the graphics card, called a shader, decides the colour and see-through amount of every pixel of that canvas, about 60 times per second.

Wherever the shader says “fully see-through”, you see the card underneath. Wherever it says “a bit of light”, it paints warm off-white on top. That is the whole trick: there is no video and no image, just one formula run on every pixel.

Each pixel only asks two questions

  1. How far am I from the moving front? The front is the bright vertical edge. Close to it, a pixel lights up a lot; far away, not at all.
  2. How long ago did the front pass over me? If it passed half a second ago, the pixel is still warm and smoky. If it passed five seconds ago, it has cooled to black.

Every layer below is built from one of those two numbers. The first question gives the edge. The second gives the trail.

trySet playback speed to 0.1 and press “Send a message”. Watch the edge cross slowly, and watch the strip under the bench light up behind it.

The six layers

The shader computes each layer as a brightness number, adds them together, and that sum becomes how opaque the pixel is.

layerwhat it doeshow, in words
lineThe razor-thin bright edge.A bell curve centred on the front that drops off very fast. The “edge sharpness” slider sets how fast. At 700 the edge is a couple of pixels wide; at 60 it turns into a soft beam.
glowThe faint haze spreading either side of the edge.The same idea with a much slower drop-off, kept dim. Sharp line plus wide glow is what makes it read as light, not as a drawn line.
wobbleThe edge is never quite straight, and it shimmers.Before measuring “how far am I from the front”, each pixel is nudged left or right by a slowly drifting smoke pattern (see “noise” below). Neighbouring rows get nudged differently, so the edge waves.
afterburnThe grey smoky trail left behind.Reads “how long since the front passed” from the memory strip and fades it with time. It's multiplied by another smoke pattern so it looks like cooling haze, not a flat grey. A small extra-hot band sits right behind the edge.
hatchThe fine diagonal stripes inside the trail.Diagonal stripes, shown only where the trail is still warm. On the real site they stand for the encrypted data passing through: you see that something is there, never what it says. Very fine stripes on a screen create the shimmering wave pattern (moiré) you noticed.
laneBrighter along the middle, dimmer at the top and bottom.Multiplies everything by a gentle curve that peaks at mid-height, as if the data travels down the centre of the card.

trySwitch off everything except “line”. Then add “glow”, then “wobble”, one at a time. You will see the effect assemble itself.

The memory strip, the one clever part

A shader has no memory. Every frame it starts from zero, so on its own it cannot know that the front crossed this spot a second ago. The trail needs that knowledge.

The fix lives in JavaScript. It keeps a row of 256 numbers, one for each thin vertical slice of the card. Think of 256 kitchen timers in a row. Whenever the front sweeps over a slice, that slice's timer is reset to “now”. Each frame, the row is handed to the graphics card as a tiny image, 256 pixels wide and 1 pixel tall, and the shader reads it to learn “how long since the front passed me”.

The strip on the bench above shows those 256 timers live: bright amber means “just crossed”, fading to black as it cools.

tryPause, then hover the strip. Each column tells you how many seconds ago a message crossed it. Then move the “cooling speed” slider and watch the trail shorten or stretch without anything moving.

The Siri colours

The original paints every layer in one warm off-white. The Siri version keeps the exact same shapes and only changes the colour each layer is painted in.

  1. A colour loop. Five colours in a ring: blue, violet, pink, orange, cyan, then back to blue. Give the loop any number and it returns a colour, blended smoothly between its two neighbours.
  2. Which colour each pixel gets. The number fed into the loop depends on the pixel's height on the card, plus the clock, plus a little smoke. So colour bands stack up the edge and slowly flow along it. The “colour bands” slider sets how many bands fit top to bottom.
  3. Each layer sits one step over. The glow takes the next colour along from the edge, and the trail the one after that, and it keeps shifting as it cools. That offset is what makes it read as an iridescent wave and not one painted stripe.
  4. A white-hot core. Where the line is at full brightness, its colour is pushed toward white, the way Siri's glow is near-white at the centre.

trySwitch off everything except “line” and “glow”, then drag “colour bands” from low to high.

Noise, the ingredient that makes it look natural

Without randomness, the edge would be a perfect line and the trail a flat grey. The shader uses fbm noise (“fractal Brownian motion”). It is a recipe for smoke-like patterns: take a blurry random pattern, add a smaller, fainter copy on top, then a smaller one again, four times. Big soft shapes with finer detail inside them, like clouds. Sliding that pattern over time makes it drift, which is what you see as shimmer.

What the JavaScript does each frame

  1. Advance the scene clock by the time since the last frame, times the playback speed.
  2. Move each message's front a little further across, using an ease-in-out curve so it speeds up then slows down.
  3. Stamp “now” into every memory-strip slot the front has just crossed.
  4. Upload the strip to the graphics card, pass in the front positions and the slider values.
  5. Draw one triangle that covers the whole canvas. That triggers the shader once for every pixel.

Up to four messages can be on the card at once, which is why rapid clicks on “Send a message” give you several fronts chasing each other.

How this compares to the original

  • Same technique: the six layers, the 256-slot memory strip, the wobble and the off-white colour.
  • Rewritten here: the code and names are mine, and every number is wired to a slider so you can explore it.
  • Not copied: on the real site the fronts are driven by the same animation clock as the dots travelling along the wires (Framer Motion), so the light and the diagram stay in sync. Here a simple timer drives it, which is enough to learn from.
  • Their efficiency trick: the site stops drawing about 6 seconds after the last message, so it costs nothing while idle. This lab draws continuously so the controls always respond.

The shader, annotated

This is the exact code running on this page. Greyed lines are comments.