Problem
All client-side JS runs in global scope. app.js exposes ~50+ variables and functions as implicit globals (play, stop, isPlaying, grid, synth, filter, etc.). jam.js consumes them by name with no declared dependency.
Consequences:
- No enforced module boundaries — any file can silently read/write any variable from any other file
- Script load order in
index.html is manually managed and fragile
- IDE tooling (go-to-definition, find-all-references, safe rename) is blind to cross-file relationships
JamSync's typeof play === 'function' guards exist purely because there's no other way to express the dependency
- Splitting
app.js into smaller files in the future requires careful global-scope archaeology
Solution
Migrate to native ES modules — a browser-native feature, no bundler or build step required.
<!-- index.html — after -->
<script type="module" src="app.js"></script>
<!-- jam.js is imported by app.js, not loaded separately -->
// app.js — export what jam.js needs
export function play(step) { ... }
export function stop() { ... }
export { isPlaying, seqPosition };
// jam.js — import explicitly
import { play, stop, isPlaying, seqPosition } from './app.js';
Acceptance Criteria
Notes
Do this last — it's the highest-leverage change but also the widest diff. Issues #3 (JamSync transport interface) and any other refactors should land first so this migration starts from a cleaner baseline.
Vendor libraries may not be ES module compatible. Tone.js has an ES module build available via npm. lz-string has a UMD build that works as a module. cytoscape has an ES module build. Each may need to be re-vendored from their module-compatible release.
Problem
All client-side JS runs in global scope.
app.jsexposes ~50+ variables and functions as implicit globals (play,stop,isPlaying,grid,synth,filter, etc.).jam.jsconsumes them by name with no declared dependency.Consequences:
index.htmlis manually managed and fragileJamSync'stypeof play === 'function'guards exist purely because there's no other way to express the dependencyapp.jsinto smaller files in the future requires careful global-scope archaeologySolution
Migrate to native ES modules — a browser-native feature, no bundler or build step required.
Acceptance Criteria
index.htmlloadsapp.jswithtype="module";jam.jsis imported byapp.js(or vice versa — establish one clear dependency direction)import/export— no implicit globals betweenapp.jsandjam.jswindow.jamSession,window.jamSendTransportetc. public API is preserved for any external integrationsTone.js,cytoscape.min.js,lz-string.min.js) are handled — either imported as modules if they support it, or loaded as non-module scripts before the module entry pointNotes
Do this last — it's the highest-leverage change but also the widest diff. Issues #3 (JamSync transport interface) and any other refactors should land first so this migration starts from a cleaner baseline.
Vendor libraries may not be ES module compatible.
Tone.jshas an ES module build available via npm.lz-stringhas a UMD build that works as a module.cytoscapehas an ES module build. Each may need to be re-vendored from their module-compatible release.