Delay Tap Display

delay-tap-displayViewers

A delay has no transfer curve — what it does is put the same sound somewhere else in time, quieter, again and again. So the picture is the train itself: a muted reference pip for the dry hit, then one pip per repeat at the moment it arrives and the height it arrives with, fading as well as shrinking so a tail five repeats deep is still readable. Two knobs that read as abstract numbers become a rhythm and a decay you can count. Set lanes to 2 and it becomes the ping-pong view: left grows up from the midline, right grows down, and the bounce reads as a zigzag without one being drawn. Which side the first repeat lands on is not a display choice — the shipped cross-coupled graph seeds only its left delay line, and the maths beside this component derives the alternation from that wiring. Supply a tempo and the beat grid appears behind the train, which is how you see repeats landing on the beat, or missing it. Computed rather than measured, so it is right before a single sample plays.

0 ms500 ms1.00 s1.50 s2.00 s2.50 s
250 ms
55%
40%
120 bpm grid
OFF

11 audible repeats above -60 dB, a 2.89 s tail. Height is level and so is opacity: by the fifth repeat a pip is two units tall, and one cue is not enough to read a tail by. The hollow pip at zero is the dry hit, whose own height is 1 - mix, because the stage crossfades rather than adding a send.

Install

Copies the source into your project. Anything it depends on comes with it.

$ npx shadcn add @syntho-ui/delay-tap-display

Pulls in

echo-traincontrol-theme

First time? Add the registry to your components.json once:

components.json
{
  "registries": {
    "@syntho-ui": "https://engine.syntho.app/r/{name}.json"
  }
}

Usage

Controlled or uncontrolled, whichever suits the surrounding state.

example.tsx
import { DelayTapDisplay } from "@/components/ui/delay-tap-display"

// A mono feedback delay at a quarter second.
<DelayTapDisplay time={0.25} feedback={0.55} wet={0.4} />

// A ping-pong: the same train, alternating sides from the left.
<DelayTapDisplay time={0.25} feedback={0.4} wet={0.5} lanes={2} />

// With the beats drawn behind it, to see what the repeats are landing on.
<DelayTapDisplay time={0.25} feedback={0.5} wet={0.4} tempo={120} division={0.5} />

Props

PropTypeDefaultDescription
timenumberSeconds between repeats. The pips sit at 1x, 2x, 3x this.
feedbacknumberLoop gain per repeat. Clamped to the audio path's own 0.95 ceiling on read, so a display fed 1.2 cannot draw a train that grows.
wetnumber1Wet/dry mix. Sets the repeats' height and the dry pip's, which is 1 - mix: the shipped stages crossfade rather than adding a send, so the dry hit shrinks as the repeats come up.
lanes1 | 211 for a mono train on a baseline, 2 for a ping-pong mirrored about the midline. Only the drawing changes: a cross-coupled pair's levels are the same feedback^(n-1) a single line produces.
firstLane0 | 10Which side the first repeat lands on. The shipped graph seeds its left line, so left is the default and nothing in the repo passes anything else.
spannumberSeconds the axis covers. Defaults to a window that fits the train with half a repeat of headroom. A fixed span also bounds the train — you get the pips that fit, not pips off the edge.
floor / maxTapsnumber1e-3 / 64Where drawing stops: an audibility cutoff at -60 dB and a hard cap. When the cap is what stopped it, the plot says so rather than implying the tail ended.
temponumber | nullBeats per minute for the sync grid. Omit for no grid, which is what every shipped delay stage gets — none of them has a tempo sync. The grid syncs nothing; it draws where the beats are so you can see whether the repeats agree.
division / beatsPerBarnumber1 / 4Sync-grid spacing in beats (1 is a quarter, 0.5 an eighth) and the bar length its labels count in.
showDrybooleantrueDraw the dry hit at t = 0. In a two-lane view it appears in both lanes: the dry path is not in the delay loop, so it keeps the source's own centred image.
colorstringOverride the accent colour for this instance.
heightnumber140Plot height in pixels.

Interaction

  • Drag with pointer capture, so the gesture keeps tracking past the edge of the control.
  • Shift while dragging for a fine pass; release it mid-gesture and coarse tracking resumes.
  • Double-click resets to defaultValue.
  • Arrow keys step normally, Shift+arrow steps finely, PageUp/PageDown jump by ten, Home/End go to the extremes.
  • Proper slider and switch roles with live aria-valuetext, so a screen reader announces the formatted value rather than the raw number.
  • The scroll wheel adjusts the value while the pointer is over the control; stepped ranges move at least one declared step per notch by default.