Build Soundtrap

KINDAreplaces $14.99/mosaves $179.88/yrback to the verdict

0%0 of 20 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A browser multitrack recorder with a click, latency-compensated recording that lands on the beat, regions you can move and trim without destroying the take, a mixer with levels and pan, and export of the mix and every stem at sample-aligned starts.

estimated effort multi-daythe files for this build are in the project pack

RuntimeTypeScript with Vite, running in the browserAudioWeb Audio API with an AudioWorklet for anything per-sampleRecordingMediaRecorder for capture, decoded to buffers for editingStorageIndexedDB for audio buffers and project stateExportOffline rendering, not real-time playback capture

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Runs the build.

    Get it nodejs.org, or nvm install 22. open ↗

    Verify node --version prints v22 or higher

  2. have readya basic interface is around 100 US dollars; measurement works without one

    Why Latency compensation can be measured with any input, but playing along to a click through browser monitoring is genuinely difficult past about twenty milliseconds, and direct monitoring removes that.

    Get it Any USB audio interface with a direct monitor control, or headphones plus a built-in microphone for measurement only.

    Verify The device appears as an input and a round trip measurement completes

  3. have readyfree with a cable you own

    Why Phase 2 needs the click to reach the input. A physical loop is the most accurate method and a microphone in front of a speaker is adequate.

    Get it A cable from output to input if your interface allows it, or place a microphone near a speaker in a quiet room.

    Verify The calibration routine produces a stable figure across three runs

  4. have readyfree

    Why Alignment is verified by looking at transients against the grid, and a sustained sound has no transient to look at.

    Get it Clapping works. So does tapping a desk.

    Verify A four-hit recording shows four clear transients

  5. installfree

    Why Metering and any per-sample work must run on the audio thread, and the worklet is how that happens.

    Get it A current Chromium, Firefox or Safari. Test in at least two, since audio behaviour differs.

    Verify An AudioWorklet loads and posts messages

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- projects(id, name, tempo_bpm, time_signature, sample_rate, created_at)
- tracks(id, project_id, name, ordinal, gain_db, pan, muted, soloed, input_device_id)
- takes(id, track_id, buffer_key, recorded_at, raw_start_ms, compensated_start_ms, duration_ms, latency_applied_ms)
- regions(id, track_id, take_id, timeline_start_ms, source_offset_ms, duration_ms, gain_db, fade_in_ms, fade_out_ms)
- calibrations(id, device_id, measured_round_trip_ms, measured_at, method)
- A region references a take with an offset. Trimming moves the region's window, never the recorded audio, so every edit stays reversible.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
VITE_DEFAULT_TEMPOrequired120Starting tempo for a new project.
VITE_SCHEDULER_LOOKAHEAD_MSrequired120How far ahead events are queued against the audio clock. Longer is safer and less responsive to tempo changes.
VITE_MAX_PROJECT_MBrequired800Warn as a project approaches this size, before the browser quota fails a recording.
VITE_MONITOR_WARN_MSrequired20Above this measured monitoring delay, warn that playing along will be difficult.
VITE_EXPORT_SAMPLE_RATErequired48000Render rate for export. Matching your recording rate avoids a resample.

The build, in order

  1. Playback on a clock

    A click plays in perfect time and does not drift over ten minutes.

    1. Schedule events a short window ahead against the audio clock, driven by a timer that only refills the queue.

      Files src/clock.ts

      terminal
      npm create vite@latest . -- --template vanilla-ts
    2. Scheduled clicks, not triggered ones. This is the pattern everything timed will use.

      Files src/metronome.ts

    3. Compare the final click's actual time against the calculated time. This is the test that catches a timer-based scheduler.

      Files src/clock.test.ts

    done when · tick each as it passes
    watch out
    • Scheduling audio with setTimeout or setInterval produces audible jitter immediately and measurable drift within minutes, because those timers are not real-time and are throttled in a background tab. Use a lookahead scheduler that queues events against the audio context's own clock, which is sample accurate.
  2. Recording that lands on the beat

    What you play on the beat is recorded on the beat.

    1. Play a click, record it through the loop, and measure the offset. Store the result per device with a timestamp.

      Files src/calibrate.ts

    2. Shift the recorded start back by the measured round trip. Store both times so the correction is inspectable.

      Files src/record.ts

    3. Four hits on the beat, then look at the transients against the grid. Do not skip the visual check.

    done when · tick each as it passes
    watch out
    • Round trip latency varies by device, browser and buffer size, so a hard coded compensation value will be right on your machine and wrong on every other one. Measure it, store it per device, and re-measure when the device changes.
    • Verify alignment visually and audibly, not by trusting the number. Record a sharp percussive sound exactly on four beats and look at where the transients sit relative to the grid.
  3. Tracks, takes and non-destructive regions

    You can record several tracks, move and trim them, and never lose a take.

    1. To IndexedDB as recording finishes. A lost take is the failure people abandon a tool over.

      Files src/store.ts

    2. Take reference, source offset, duration. Trimming edits the window only.

      Files src/regions.ts

    3. Reference counting, so deleting one region does not destroy another's source.

      Files src/takes.ts

    done when · tick each as it passes
    watch out
    • Trimming by rewriting the audio buffer destroys the material outside the trim, and the first time someone trims too far they have lost the take. A region is a window onto a take: trimming moves the window, and dragging the edge back out restores what was there.
  4. Monitoring and the mixer

    You can hear yourself while recording without the delay making it impossible to play.

    1. Gain and pan per track into a master. Solo is a mute of everything else, computed rather than stored per track.

      Files src/mixer.ts

    2. An AudioWorklet computing levels and posting them, so metering never blocks audio.

      Files src/meter-worklet.js

    3. Show the measured number next to the toggle. It is the difference between a usable session and a frustrating one.

      Files src/monitor.ts

    done when · tick each as it passes
    watch out
    • Monitoring your own voice through the browser adds the full round trip to what you hear, and past roughly twenty milliseconds that delay makes singing or playing genuinely difficult. Offer the option, state the measured delay next to it, and recommend hardware or direct monitoring where the interface provides it.
  5. Export, mix and stems

    What you export is what you heard, and the stems line up when reassembled.

    1. An OfflineAudioContext rendering the graph. Faster than real time and deterministic, unlike capturing playback.

      Files src/export.ts

    2. Same length, same start, leading silence included. This is what makes them portable.

      Files src/export.ts

    3. Sum the stems and compare against the mix numerically. A difference means a gain or pan is applied in one path and not the other.

      Files src/export.test.ts

    done when · tick each as it passes
    watch out
    • Exporting stems trimmed to each track's first sound means they no longer share a start, and dropping them into another tool puts everything in the wrong place. Every stem starts at timeline zero including its leading silence, which is what makes them usable elsewhere.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more music & audio.