Sounds editor — keyboard reference

The Sounds tab is a tracker-style pattern editor. Click any cell in the 32×5 pattern grid to place the cursor, then use the keymap below to write notes, instruments, volumes, and stops. The cursor's sub-column (Note / Inst / Vol) determines which keys are active.

Notes — FT2 two-octave keymap

Two rows on the keyboard map to two consecutive octaves, anchored on the editor's current octave (default 4). The lower row (Z–M plus ", L .") covers C through D# of the next octave; the upper row (Q–U plus 2-7) covers C through B of the next octave; I–P plus 9 0 reach the third octave's C through E.

KeyNoteOctave offset
ZC+0
SC#+0
XD+0
DD#+0
CE+0
VF+0
GF#+0
BG+0
HG#+0
NA+0
JA#+0
MB+0
,C+1
LC#+1
.D+1
QC+1
2C#+1
WD+1
3D#+1
EE+1
RF+1
5F#+1
TG+1
6G#+1
YA+1
7A#+1
UB+1
IC+2
9C#+2
OD+2
0D#+2
PE+2

Pressing a note key writes the note at the cursor, advances the cursor down one row, and live-previews the note via the cell's instrument.

Stop / clear

KeyEffect
=Note-off — triggers ADSR release; voice rings through release phase, then silences.
-Note-cut — immediate silence, no release ramp.
Backspace / DeleteClear the active sub-column at the cursor (Note / Inst / Vol).

Octave shift

KeyEffect
<Lower the editor's base octave (clamped to 0).
>Raise the editor's base octave (clamped to 8).

The current octave is shown in the transport bar at the bottom of the Sounds tab. Default is 4 (so Z = C4).

Hex digits — Inst and Vol sub-columns

When the cursor is on the Inst or Vol sub-column, 0–9 and A–F write hex values.

Sub-columnBehavior
Inst (2 hex digits)Shift-register: each keypress shifts the existing value left and appends the new digit. Type 0 then 5 to enter 05. Cursor does NOT auto-advance, so you can build a 2-digit instrument id without losing the row.
Vol (1 hex digit)Replace: each keypress overwrites the current value. Cursor does NOT auto-advance.

Navigation

KeyEffect
/ Move cursor up / down one row.
/ Move cursor left / right one sub-column (wraps across channel boundaries).
TabMove cursor right (same as →).
Shift+TabMove cursor left (same as ←).
HomeJump to row 0, channel 0, Note sub-column.
EndJump to last row of the current channel + sub-column.
PageUp / PageDownJump 8 rows up / down.

Undo / redo

KeyEffect
Ctrl+Z / ⌘+ZUndo.
Ctrl+Shift+Z / ⌘+Shift+ZRedo.
Ctrl+YRedo (alternate).

Undo snapshots the entire SOND data on each meaningful change. Typing bursts are debounced with a 250 ms window so a sequence of note keypresses collapses into one undo step. The undo stack is capped at 50 snapshots; older snapshots drop FIFO.

Transport buttons

Below the pattern grid the transport bar shows the current pattern, row, and BPM. Buttons:

ButtonEffect
▶ PlayStart the host engine on the active pattern (loops; same as cart-Forth's play-pattern).
■ StopHalt the engine and all SFX slots (panic button). Equivalent to stop-music followed by -1 stop-sfx.
⊙ FollowToggle whether the cursor tracks the playhead row while the engine is running.

Music and SFX buses

As of P5.2 the host tracker engine has two parallel buses: a music slot that loops your background track, and four SFX slots that overlay one-shot or looping sound effects on top. Each slot reads patterns from the same SOND pool — the distinction is entirely about which cart-Forth word you call, not how you author the pattern.

Music bus: ( pattern -- ) play-music starts a looping pattern; ( -- ) stop-music halts it. play-pattern is an alias retained from pre-P5.2 — both names produce identical bytecode.

SFX bus: ( pattern slot -- ) play-sfx fires a one-shot pattern on slot 0–3 (or -1 to auto-allocate the first idle slot, evicting the oldest if all four are busy). loop-sfx ( pattern slot -- ) is the looping variant. ( slot -- ) stop-sfx stops one slot; -1 stop-sfx stops all SFX without touching music.

When two slots write to the same voice on the same tick, the engine dispatches in fixed order — music first, then SFX 0, 1, 2, 3 — and the last write wins. Authors who want non-conflicting layering can author SFX patterns to use voices the music doesn't touch (typically the sawtooth + noise channels).

play-once (pre-P5.2 single-pattern player) is retained as a shorthand: it's equivalent to passing your pattern number followed by -1 to play-sfx. For example, 2 play-once and 2 -1 play-sfx compile to equivalent behavior — both fire pattern 2 on an auto-allocated SFX slot. The semantic shift is intentional — pre-P5.2 it cut music; post-P5.2 it overlays.

On GameTank carts the same music and SFX also play in native ROM builds (the studio's Export .gtr) — the ROM embeds a tracker sequencer driving the audio coprocessor's fixed-function mixer firmware, and tempo stays locked to 60 Hz wall-time even when a frame runs over budget. Two voice counts are easy to conflate here: the GameTank machine profile itself reports 4 voices — a conservative, browser-side software-mix cap the studio and cost model use for portability warnings — while the native ACP mixer firmware implements 5 hardware voice slots, one per SOND channel role (sq0, sq1, triangle, sawtooth, noise). A cart's browser playback and its native .gtr playback both draw on the same SOND patterns either way; the 4-vs-5 split is a browser-estimate-vs-hardware-reality distinction, not a feature you author around.

Dynamic tempo

A cart can change the music slot's tempo at any time by calling ( frames-per-row -- ) set-bpm. The new tempo (clamped to 2..=64) takes effect at the next row boundary. SFX slots are never affected.

Call set-bpm every frame to drive smooth tempo ramps — useful for Space-Invaders-style march acceleration or any rhythm-based gameplay that responds to game state.

\ Halve the music tempo from frame ~60 onward.
variable frame
: update
    frame @ 1 + frame !
    frame @ 60 = if 4 set-bpm then
;

set-bpm is a no-op when no music is playing — it does not pre-stage a tempo for a future play-music; that call always re-reads the cart's SOND header tempo.

Sample instruments (GameTank)

GameTank carts can back an instrument with an imported PCM sample instead of a synth voice. The instrument editor's Kind select — Synth / Sample — only appears on GameTank carts; SC8 has no PCM samples, so its instruments stay synth-only. Switching Kind keeps the instrument's channel hint and resets everything else to a fresh preset for the new kind.

Sample-kind instruments replace the ADSR + volume sliders above with three controls:

ControlEffect
SmpSlot picker over the cart's Samples-tab table (up to 16 slots), each option labeled with its duration. An empty table shows a hint pointing at the Samples tab; a stale reference (e.g. after a slot delete) shows a disabled placeholder plus a warning line.
PitchBase pitch, 0–127, read and written as a MIDI note number with a live name readout next to the slider (e.g. 69 · A4). Cart-Forth triggers the sample repitched relative to this note.
CHThe same channel-hint select synth instruments use, reused unchanged.

Sample instruments are audible in-studio the same two ways as synth instruments: the ▶ test button previews MIDI note A4 on the instrument's channel, and the transport bar's ▶ Play button plays back any pattern that references the instrument — exactly as it will sound on /play.

Import rate (rate_code)

A sample instrument's pitch and slot live here in the Sounds tab, but the rate the underlying audio was captured at is chosen back in the Samples tab, at import time. Every import is decoded, downmixed to mono, and resampled to one of four fixed rates:

rate_codeRate
022050 Hz (default)
111025 Hz
25513 Hz
344100 Hz

A lower rate costs proportionally fewer bytes per second of audio — Samples & sample instruments covers the import pipeline, the waveform editor, and loop markers in full. The number that matters from the Sounds tab's side: all of a cart's samples must together fit in 2,048 bytes of audio-coprocessor RAM — the budget left over on native hardware after the ACP mixer firmware and its zero-page / stack pages — or a native ROM export (Export .gtr) will reject the cart. Browser playback is unaffected either way; the Samples tab surfaces this as a non-blocking warning once the total is exceeded.