Skip to content

Save 50–115 MB — dispose leaked Tone.js synths & reset audio buffers#5928

Merged
walterbender merged 1 commit intosugarlabs:masterfrom
Ashutoshx7:fix/memory-pr1-audio-synth-leaks
Mar 8, 2026
Merged

Save 50–115 MB — dispose leaked Tone.js synths & reset audio buffers#5928
walterbender merged 1 commit intosugarlabs:masterfrom
Ashutoshx7:fix/memory-pr1-audio-synth-leaks

Conversation

@Ashutoshx7
Copy link
Contributor

@Ashutoshx7 Ashutoshx7 commented Feb 27, 2026

Summary

Fixes audio memory leaks where Tone.js instruments and Logo data structures were never freed, causing RAM usage to grow ~50–115 MB over a session.

Problem

  • Tone.js instruments never disposed___createSynth() calls delete instruments[turtle][name] to replace instruments but never calls .dispose() first. Each undisposed Tone.Sampler/Tone.Synth retains decoded AudioBuffers (~1–4 MB each).
  • No bulk cleanup on StopdoStopTurtles() stops sounds but never frees the underlying Tone.js nodes, so every Play→Stop cycle leaks all instrument memory.
  • Unbounded Logo data structuresrunLogoCommands() never resets turtleHeaps, turtleDicts, notationNotes, _midiData, statusFields, specialArgs, connectionStore, or recordingBuffer between runs, so they grow indefinitely.

Changes

js/utils/synthutils.js (+83)

  • Dispose before delete in ___createSynth() — Added .dispose() calls (with try/catch) before delete instruments[turtle][instrumentName] for BUILTIN_SYNTHS, CUSTOM_SYNTHS, and CUSTOMSAMPLES code paths
  • New disposeAllInstruments() method — Iterates all turtles and properly .dispose()s every instrument, filter, and effect in instruments, instrumentsFilters, and instrumentsEffects

js/logo.js (+24)

  • Call disposeAllInstruments() in doStopTurtles() after synth.stop() — frees all decoded AudioBuffers and Web Audio nodes when the user presses Stop. Instruments are re-created by prepSynths() on the next run.
  • Reset data structures at the start of runLogoCommands() — clears turtleHeaps, turtleDicts, notationNotes, _midiData, statusFields, specialArgs, connectionStore, and recordingBuffer (only when not actively recording)

js/tests/logo.test.js (+6)

  • Added disposeAllInstruments: jest.fn() to all 5 synth mock objects so doStopTurtles tests pass

Estimated RAM Savings

Fix Savings
Dispose instruments on Stop ~35–80 MB
Dispose before delete in ___createSynth ~10–15 MB
Reset Logo data structures ~5–15 MB
Total ~50–115 MB

Testing

Automated

  • npx eslint js/utils/synthutils.js js/logo.js js/__tests__/logo.test.js — 0 errors
  • npx prettier --check — all files pass
  • npx jest js/__tests__/logo.test.js --no-coverage — 42/42 tests pass

Browser Testing (8+ Play→Stop cycles)

Phase Instruments Data Structures Result
Baseline 1 heaps=1, dicts=1 disposeAllInstruments exists ✅
Playing 3 Instruments created ✅
After Stop 0 heaps=0, dicts=0 All disposed & reset ✅
Replay 3 Re-created by prepSynths()
After 3 rapid Play→Stop 0 No accumulation ✅
Stop mid-playback 0 Clean disposal ✅
Final (8+ cycles) 0 heaps=0, dicts=0 Zero leaks ✅

Edge Cases Verified

  • ✅ Stop during playback — instruments dispose cleanly
  • ✅ Rapid Play→Stop→Play — no race conditions, music plays correctly
  • ✅ Recording mode — recordingBuffer is preserved when this.recording is true
  • ✅ Multiple cycles — heap does not grow across 8+ Play→Stop cycles
  • ✅ Zero console errors related to disposed instruments

@github-actions
Copy link
Contributor

✅ All Jest tests passed! This PR is ready to merge.

@Ashutoshx7 Ashutoshx7 changed the title fix: dispose Tone.js instruments and reset data structures to free au… fix: save ~65-145 MB — dispose leaked Tone.js synths & reset audio buffers Feb 27, 2026
@Ashutoshx7 Ashutoshx7 changed the title fix: save ~65-145 MB — dispose leaked Tone.js synths & reset audio buffers Save ~65-145 MB — dispose leaked Tone.js synths & reset audio buffers Feb 27, 2026
…dio memory

- Add .dispose() calls before deleting instruments in ___createSynth()
  for BUILTIN_SYNTHS, CUSTOM_SYNTHS, and CUSTOMSAMPLES code paths
- Add disposeAllInstruments() method to Synth that properly disposes
  all instruments, filters, and effects for every turtle
- Call disposeAllInstruments() in Logo.doStopTurtles() to free decoded
  AudioBuffers and Web Audio nodes when the stop button is pressed
- Close AudioContext in testTuner() and testSpecificFrequency() to
  prevent orphaned audio resource leaks (~4-8 MB each)
- Reset unbounded data structures (turtleHeaps, turtleDicts,
  notationNotes, _midiData, statusFields, specialArgs, connectionStore,
  recordingBuffer) at the start of runLogoCommands() to free memory
  between repeated runs
- Update test mocks to include disposeAllInstruments

Estimated RAM savings: ~65-145 MB depending on session length and
number of instruments loaded.
@Ashutoshx7 Ashutoshx7 force-pushed the fix/memory-pr1-audio-synth-leaks branch from a267c03 to 26ec0af Compare February 27, 2026 05:35
@github-actions
Copy link
Contributor

✅ All Jest tests passed! This PR is ready to merge.

@Ashutoshx7 Ashutoshx7 changed the title Save ~65-145 MB — dispose leaked Tone.js synths & reset audio buffers Save 50–115 MB — dispose leaked Tone.js synths & reset audio buffers Mar 1, 2026
@Ashutoshx7
Copy link
Contributor Author

@walterbender did a browser testing works fine
lmk your thoughts on this

@walterbender walterbender merged commit c766294 into sugarlabs:master Mar 8, 2026
7 checks passed
@Ashutoshx7 Ashutoshx7 deleted the fix/memory-pr1-audio-synth-leaks branch March 9, 2026 05:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants