Stample Recording & Playback Bug Report #
Date: 2026-02-07
File: system/public/aesthetic.computer/disks/stample.mjs
Symptoms #
- Recording "only works once" — after the first recording, the strip buttons stop responding
- Audio goes silent, especially when switching between stample and notepat
- Sample playback doesn't recover after navigating away and back
Root Causes Found #
Bug 1: Strip Button Stuck in down=true State (PRIMARY) #
Location: act() → strip button up handler
Severity: Critical
The up callback returned false when e.pointer !== btn.downPointer (pointer ID mismatch between touch-down and lift events). The UI system (ui.mjs line 459) interprets return false as "keep btn.down = true". This permanently blocked future presses because the down handler checks if (btn.down) return false; as a guard.
On touch devices, pointer IDs can change between down and up events. This made the button permanently stuck after one use.
Fix: Always return true from the up handler — kill the sound unconditionally and release the button.
Bug 2: Dead Sound References in sounds[] Array #
Location: act() and sim()
Severity: Medium
After sounds[index]?.kill(0.1), the array entry was NOT cleared (sounds[index] remained a dead sound object). In sim(), sounds.find(s => s) found these killed sounds, causing bitmapProgress tracking to poll dead objects.
Fix: Set sounds[index] = undefined after killing.
Bug 3: Aggressive Mic Disconnect on Every Strip Press #
Location: act() → strip button down handler
Severity: High
Every strip button press called sound.microphone.disconnect(), which:
- Called
detachMicrophone()in bios - Disconnected the mic processor and mic node
- Stopped ALL media stream tracks (
micStream.getTracks().forEach(t => t.stop())) - Set
microphone.connected = false
This completely destroyed the mic connection, requiring a full getUserMedia re-request. But micRecordButtonLabel wasn't updated to "Connect", so users thought they could record again immediately (it still said "Record"). They'd press Record, but it would reconnect instead. They had to press twice to record again.
Fix: Only interrupt the mic if it's actively recording (call cut() to stop the partial recording). Leave the connection intact otherwise. Also handle the "microphone-disconnect" event to update the button label.
Bug 4: microphone-disconnect Not Dispatched as Act Event #
Location: system/public/aesthetic.computer/lib/disk.mjs line 9631
Severity: Medium
The "microphone-disconnect" message from bios was handled in the disk worker but NOT forwarded to actAlerts. So e.is("microphone-disconnect") in stample's act() never fired, meaning the button label couldn't be updated on disconnect.
Fix: Added actAlerts.push("microphone-disconnect") in the handler.
Bug 5: No leave() Export for Cleanup #
Location: module exports
Severity: Low-Medium
Stample had no leave() function. When jumping to notepat, playing sounds and loops weren't explicitly killed. The bios does handle cleanup (killAllSound, clearSoundSampleCache, detachMicrophone), but having explicit cleanup prevents timing issues.
Fix: Added leave() that kills all active sounds, loops, and resets state.
Notepat Interaction #
When jump("notepat:stample") is called:
- Bios
detachMicrophone()disconnects the mic - Bios
killAllSound()stops all speaker worklet sounds - Bios
clearSoundSampleCache()clears the worklet's cached buffers - Bios deletes all
sfx[]entries except the defaultsound
Notepat then boots and:
- Loads the stample sample from IndexedDB (
store["stample:sample"]) - Re-registers it via
sound.registerSample() - Sets
wave = "stample"from the colon parameter
When returning to stample:
boot()re-runs, resets all state- Preloads
"startup"(default sample) first - Then retrieves stored sample from IndexedDB, overriding the default
- Re-registers the sample
This flow is correct and should work, provided the IndexedDB store was persisted. The persist calls in the recording up handler look correct.
Git History Summary #
| Date | Commit | Description |
|---|---|---|
| 2025-01-29 | 86cbd7d6 | Initial creation as sprample |
| 2025-01-29 | b5adea25 | Renamed to stample, sample spreading WIP |
| 2025-01-29 | 1e9d4380 | Add pats parameter support |
| 2025-01-30 | d87a5a22 | Stample update |
| 2025-02-04 | 7424d24c | Pitch shift test |
| 2025-02-04 | 63c31cd5 | Y-axis scrub test |
| 2025-02-23 | cf10ff3d | Sound system updates |
| 2025-03-02–07 | Multiple | Multi-touch, general WIP |
| 2025-03-25 | 7456c038 | Resize/framing fixes |
| 2025-03-29 | e3380ebe | Quick update for demo |
| 2025-03-30 | 346e72c0 | Stample todos |
| 2025-06-27 | 3ddb6d5f | UI pen updates |
| 2025-09-30 | 75d3cc36 | Fix stample scrub |
| 2026-01-24–26 | Multiple | Major refactor: responsive layout system, bitmap loading, notepat integration |
| 2026-01-27–28 | Multiple | KidLisp $code embedding, GPU effects, live audio updates |
| 2026-02-01 | c5d093e2 | Scrub debugging |
| 2026-02-05 | f15dd3f5 | Merge: accept remote stample.mjs |
| 2026-02-06 | 05010c1e | Fix duplicate floor declaration |
| 2026-02-07 | 806b4d04 | Stample fix (layout constants, KidLisp support) |
Files Changed #
system/public/aesthetic.computer/disks/stample.mjs— 4 bug fixessystem/public/aesthetic.computer/lib/disk.mjs— 1 fix (microphone-disconnect event dispatch)