Field Sampler — interface walkthrough

branch develop · commit 5a8d358 · Wed, 14 Oct 2026 23:36:51 GMT · iPad Air (5th gen) landscape, 1180×820 pt @2×, touch; Chromium · CI run

105/109 4 checks failed 27 steps. Audio is what the app sent to the speakers, captured in the page. Grey (i) rows are measurements to look at, not pass/fail.

Listen through 23 clips · 0:37

Every sound the walkthrough played, in order, in one file: a soft tick before each clip. Tap a row to jump to it; the row playing is highlighted. Download to listen anywhere.

NowPress play, or tap a clip.

1Measuring instruments0.3 s

Before measuring the app, the walkthrough's own pitch estimator (analyze.mjs: FFT peaks of the first 8 harmonics; no code shared with the app's pitch detection) is checked on synthetic tones of known pitch: plucks and decaying harmonic tones, 130–880 Hz, with noise, each searched from a hint 100¢ off.

2First launch0.4 s

The app opens on a blank session: no dialogs, a prompt to record or import, and every control in place.

Empty session on an iPad Air in landscape (1180×820 pt).
Empty session on an iPad Air in landscape (1180×820 pt).

3Demo take0.7 s

Demo loads a built-in synthetic recording (knocks, thuds, plucked notes) so the app is playable without a microphone.

The demo take, sliced.
The demo take, sliced.

4Record from the microphone3.1 s

Rec starts recording at once; the waveform scrolls in live. Rec again stops, analyses and saves the take. (The microphone here is a fake device playing the demo file.)

Recording: live waveform, elapsed time on the Rec button.
Recording: live waveform, elapsed time on the Rec button.
Recorded take, sliced and saved.
Recorded take, sliced and saved.

5Import a file1.0 s

Import opens a file (here the walkthrough take: footsteps, knocks, a plucked A3 29 cents sharp, a quiet knock, a wind gust, glass, bird chirps, dropped keys, a bell and a faint knock). Analysis runs off the main thread and the take is saved.

Ground truth: footstep 0.4s · footstep 1s · footstep 1.6s · knock 2.3s · knock 2.55s · pluck 3.2s · quiet knock 5.8s · glass 6.4s · bird chirp 7.6s · keys 8.3s · footstep 9.3s · bell 10.3s · faint knock 12.1s

The imported file itself.
Imported take, sliced. Faint audio is trimmed background no pad plays.
Imported take, sliced. Faint audio is trimmed background no pad plays.

6Keys arm the most tonal slice0.6 s

After analysis the keys arm the slice that makes the best instrument (pitch confidence × ring length), not slice 1. Its detected root labels the keys.

Keys panel: the armed slice, its root, and the root key (●).
Keys panel: the armed slice, its root, and the root key (●).

7Pads play trimmed slices1.3 s

A pad plays its slice from just before the attack to where the sound dies into the background, not to the next hit. Faint regions on the waveform are what gets trimmed.

Pad 1: the first footstep. Level, dBFS

Listen for: A short footstep thump that stops cleanly: no room hiss trailing after it.

-10-30-50-700.0s0.5s1.0snext hit
Pad flashes on touch; its slice is selected on the waveform.
Pad flashes on touch; its slice is selected on the waveform.

8Normalization5.0 s

With Norm on (default), every slice plays at the same peak level, so a distant knock is as playable as a close one, up to a +18 dB lift: a near-silent slice is not turned into loud hiss. Norm off plays slices at their recorded level.

Knock, Norm on. Level, dBFS

Listen for: A knock at full level.

-10-30-50-700.0s0.5s
Quiet knock (10 dB softer in the file), Norm on. Level, dBFS

Listen for: Another knock, as loud as the previous one (it is 10 dB softer in the file).

-10-30-50-700.0s0.5s
Faint knock (24 dB softer in the file), Norm on. Level, dBFS

Listen for: A faint knock (24 dB down in the file), Norm on: lifted, but clearly softer than the knocks before it (the lift stops at +18 dB).

-20-40-60-800.0s0.5s
Knock, Norm off. Level, dBFS

Listen for: Norm off: the knock at its recorded level.

-10-30-50-700.0s0.5s
Quiet knock, Norm off. Level, dBFS

Listen for: Norm off: clearly softer than the previous knock.

-20-40-60-800.0s0.5s
Faint knock, Norm off. Level, dBFS

Listen for: Norm off: the faint knock at its recorded level, barely there.

-30-50-70-900.0s0.5s
Norm toggled off (bottom right of the pads).
Norm toggled off (bottom right of the pads).

9Waveform: tap, double-tap, arrows3.9 s

Tapping a slice selects and auditions it (Delete acts on the selection). Double-tapping arms it on the keys. The ‹ › arrows step the armed slice. A glass and a bell have no clean harmonic series, so their roots are best guesses, marked ≈ with no cents: the glass by its lowest partial, the bell by its hum (the right note, an octave below the strike).

Tap on the second knock: auditions it. Level, dBFS

Listen for: The second knock, auditioned by tapping its slice on the waveform.

0-20-40-600.0s0.5s
Double-tap armed the glass slice.
Double-tap armed the glass slice.
Double-tap armed the bell: its root is a best guess, marked ≈.
Double-tap armed the bell: its root is a best guess, marked ≈.

10Keys: pitch matches the label8.2 s

Each key plays the armed slice at that key's pitch, tuned against the detected root, cents included: the pluck is 29 cents sharp, and its A3 key plays A3, not A3 +29¢. Touching low on a key plays it dry (no smear). Measured: the pitch of what comes out of the speakers, by the walkthrough's own estimator (step 1), to ±2¢.

Key root (MIDI 57), held 1 s, dry. Level, dBFS

Listen for: The plucked string at A3, in tune (the recording is 29 cents sharp; the key corrects it), steady, no warble.

-10-30-50-700.0s0.5s1.0s
Key lowest (MIDI 48), held 1 s, dry. Level, dBFS

Listen for: The same pluck lower (C3), same character, no warble.

-10-30-50-700.0s0.5s1.0s
Key highest (MIDI 60), held 1 s, dry. Level, dBFS

Listen for: The same pluck higher (C4), same character, no warble.

-10-30-50-700.0s0.5s1.0s

11Keys: a held note sustains9.6 s

Held past the end of its slice, a pitched note that rings plays its natural decay, then hovers where it has fallen 12 dB: each grain comes from a random in-tune spot there, so there is no fixed loop to hear repeating. Release fades it out. Fast-decaying and unpitched sounds have nothing to sustain: held, they play once and end (see MIDI).

Root key held 4 s, then released. Level, dBFS

Listen for: The pluck rings, then settles into a steady, slightly grainy tone: no pulse, no audible loop, no re-strike. It fades at release.

-10-30-50-700.0s1.0s2.0s3.0s4.0srelease
Holding the root key: the key lights, grains show on the waveform.
Holding the root key: the key lights, grains show on the waveform.

12Smear7.2 s

Finger height on a key is the smear: low is dry, high freezes the sound into a grain cloud (live PaulStretch). Smeared notes release more slowly.

Root key touched near the top (smear ≈ 0.9), held 2.5 s. Level, dBFS

Listen for: A frozen, washy cloud made from the pluck, with a slow fade after release.

-10-30-50-700.0s1.0s2.0s3.0s4.0srelease
Smeared note: grain dots across the slice on the waveform.
Smeared note: grain dots across the slice on the waveform.

13Computer keyboard3.5 s

With a hardware keyboard: number row plays pads, A…K plays the keys, ← → step the armed slice, Z X shift octave.

Key 1 → pad 1. Level, dBFS

Listen for: Pad 1 (a footstep), from the hardware key 1.

-10-30-50-700.0s0.5s
A key held 1 s (lowest key). Level, dBFS

Listen for: The lowest key (C3), from the hardware key A.

-10-30-50-700.0s0.5s1.0s

14MIDI14.9 s

MIDI channel 1 plays pads from C1 (note 36); channels 2–16 play slices 1–15 pitched; velocity sets level; mod wheel sets smear. (Driven through the hook a native iPad wrapper uses: window.fieldSamplerMidi.)

Ch 1 note 36, velocity 127 → pad 1. Level, dBFS

Listen for: MIDI pad 1 at full velocity.

-10-30-50-700.0s0.5s
Ch 1 note 36, velocity 64. Level, dBFS

Listen for: The same pad, clearly softer (velocity 64).

-20-40-60-800.0s0.5s
Ch 1 note 36 twice at once, velocity 127: pad 1 doubled. Level, dBFS

Listen for: The same pad, louder but clean: two hits summed, held under full scale.

-10-30-50-700.0s0.5s
Ch 7 note 69 (A4) → slice 6, the pluck, an octave up. Level, dBFS

Listen for: The pluck an octave up (A4), from a MIDI channel.

-10-30-50-700.0s0.5s1.0s
Ch 7 notes 69, 73, 76 (A major) at velocity 127, held 1 s. Level, dBFS

Listen for: A three-note chord on the pluck, clean: no crackle where the notes sum.

-10-30-50-700.0s0.5s1.0s
Ch 5 note 60 → slice 4, a knock, held 1.5 s. Level, dBFS

Listen for: A knock on a MIDI channel, held: it plays once and stops, no sustain.

-10-30-50-700.0s0.5s1.0s1.5s2.0snote off
Same note with the mod wheel up (smear 1), held 1.5 s. Level, dBFS

Listen for: The same note with the mod wheel up: smeared, with a long tail.

-10-30-50-700.0s0.5s1.0s1.5s2.0s2.5snote off

15Delete and undo6.3 s

Delete a transient with the trash button under its marker (or the Delete key). Its audio is dropped; Undo brings it back.

Second knock selected: the trash button sits under its marker.
Second knock selected: the trash button sits under its marker.
After the trash tap: one hit fewer.
After the trash tap: one hit fewer.

16Add a transient6.2 s

Long-press on the waveform adds a transient at the finger. It gets its own natural end like any other hit, and its own pad. Added here in the middle of the ringing pluck, its pad starts mid-note: a 2 ms fade-in keeps that from clicking.

Pad 7: the added hit, mid-pluck. Level, dBFS

Listen for: A hit added by hand in the middle of the ringing pluck: it starts mid-note, softly (a 2 ms fade-in), with no click.

-10-30-50-700.0s0.5s
A transient added by long-press at 3.9 s, inside the ringing pluck.
A transient added by long-press at 3.9 s, inside the ringing pluck.

17Sensitivity7.1 s

The slider beside the waveform filters precomputed candidates: instant, no re-analysis. Short ticks along the bottom show what the next stretch of the slider would add. The setting is saved with the take.

Sensitivity near the top: more hits.
Sensitivity near the top: more hits.
Sensitivity below the default: the weakest hit drops out.
Sensitivity below the default: the weakest hit drops out.

18Name, history and link3.0 s

Tap the name to rename. The ▾ menu lists this browser's takes and copies the take's link. Edits autosave.

Takes menu: Copy link and this browser's takes.
Takes menu: Copy link and this browser's takes.

19Shared link: view only, with a preview card0.8 s

Someone opening the link (another browser) gets the same take and can play it, but not change it. Messages and Slack show a preview card with the name and waveform.

The link opened in a fresh browser: "View only".
The link opened in a fresh browser: "View only".
The preview card image (og:image).
The preview card image (og:image).

20Reopen a take from history1.4 s

Choosing a take in the menu opens it (its URL becomes the address), with its own slicing and edits.

The recorded take, reopened from the menu.
The recorded take, reopened from the menu.

21Switching takes keeps each take whole10.1 s

Opening another take, or starting a new one, never writes one take's edits into another, never loses an edit made just before the switch, and never lets a slow upload take over the address of the take on screen.

22Edits survive reopening3.7 s

A deletion is part of the saved take: opened again later (here: the page reloaded), the take comes back without the deleted hit, on screen and on the server.

The recorded take after a reload: the deleted hit stays deleted.
The recorded take after a reload: the deleted hit stays deleted.

23Inside the iPad app: the native engine plays5.2 s

The same page with a stand-in for the iPad app's bridge (window.fieldSamplerNative and a message handler that records what it is sent). The page must hand the take to the native engine (instrument.set: its id, slice table, roots and Norm gains, armed slice), send pads and keys as messages, and make no sound of its own. The real bridge is tested in the iPad simulator (ios/scripts/build.sh --self-test-web).

24Inside the iPad app: native recording and import3.7 s

The app records and imports natively (features record and import): Rec is back in the app, the recording's live peaks come from native, and on stop the page reads the file through the bridge (capture.read, in slices) and saves it as a take on the server, exactly like an import. Import opens the app's document picker instead of the web file input. A refused microphone shows a message. Nothing uses getUserMedia or Web Audio output. Stand-in bridge here; the real one is tested in the simulator (ios/scripts/build.sh --self-test-record).

Recording natively: live peaks from the app.
Recording natively: live peaks from the app.

25Inside the iPad app: takes in iCloud Drive7.1 s

With the take library (feature library), new takes go to the person's iCloud Drive through the bridge, never to the server; the menu lists them, then the older server takes, marked by where they live. Switching takes keeps each take whole here too. Sharing uploads that one take on demand and keeps its link current. Stand-in library here; the real one is tested in the simulator (ios/scripts/build.sh --self-test-library).

The takes menu in the app: iCloud Drive takes, then older server takes.
The takes menu in the app: iCloud Drive takes, then older server takes.

26Portrait2.1 s

Portrait works too (secondary): the same single screen, stacked.

iPad Air in portrait (820×1180 pt).
iPad Air in portrait (820×1180 pt).

27Console0.0 s

Errors the page logged during the whole walkthrough.

About the demo take

13 s, stereo, 48 kHz, built by e2e/fixture.mjs. Room tone ~−50 dBFS with a +0.01 DC offset and 6 Hz rumble; a wind gust 4.5–5.4 s. Hits: footstep 0.4 s · footstep 1 s · footstep 1.6 s · knock 2.3 s · knock 2.55 s · pluck 3.2 s (223.78 Hz) · quiet knock 5.8 s · glass 6.4 s (880.00 Hz) · bird chirp 7.6 s · keys 8.3 s · footstep 9.3 s · bell 10.3 s (440.00 Hz) · faint knock 12.1 s.