Strudel Integration Analysis for KidLisp Jukebox #
Overview #
Strudel is a web-based live coding environment for algorithmic music patterns, a JavaScript port of TidalCycles. It's licensed under AGPL-3.0, which requires source code disclosure for network services.
Repository: https://codeberg.org/uzu/strudel (cloned to reference/strudel/)
Integration Options #
Option 1: @strudel/embed - Iframe Embed (Easiest) #
Load Strudel REPL in an iframe:
<script src="https://unpkg.com/@strudel/embed@latest"></script>
<strudel-repl>
<!--
s("bd sd hh sd").play()
-->
</strudel-repl>
Pros: Simplest, fully isolated, version pinning
Cons: No access to audio output for analysis, no KidLisp integration
Option 2: @strudel/repl - Direct Web Component #
<script src="https://unpkg.com/@strudel/repl@latest"></script>
<strudel-editor id="repl"><!--
s("bd sd hh sd").play()
--></strudel-editor>
<script>
const repl = document.getElementById('repl');
// Access editor: repl.editor.start(), .stop(), .evaluate(code)
</script>
Pros: Direct access to REPL API, can control playback
Cons: Still no direct audio node access
Option 3: @strudel/web - Headless Mode (Best for Integration) #
<script src="https://unpkg.com/@strudel/web@1.3.0"></script>
<script>
initStrudel({
prebake: () => samples('github:tidalcycles/dirt-samples'),
});
// Evaluate and play
evaluate(`s("bd sd hh sd").play()`);
// Or use pattern API directly
s("bd sd hh sd").play();
// Stop
hush();
</script>
Pros: Full control, can inject custom audio routing
Cons: Requires understanding Strudel pattern API
Audio Analysis Integration #
Key Files in Strudel #
-
packages/superdough/audioContext.mjs:getAudioContext()- Returns shared AudioContextsetAudioContext(ctx)- Set custom AudioContext
-
packages/superdough/superdough.mjs:analysers- Map of AnalyserNodes by IDgetAnalyserById(id, fftSize, smoothingTimeConstant)- Get/create analysergetAnalyzerData(type, id)- Get 'time' or 'frequency' data
-
packages/superdough/superdoughoutput.mjs:SuperdoughOutput- Main output controllerSuperdoughAudioController- Manages orbits (channels) and busesdestinationGain- Final output gain node before destination
Hooking Into Audio Output #
To get audio analysis from Strudel, we need to:
- Access the destination gain node:
import { getAudioContext } from '@strudel/superdough';
// After initStrudel(), the audio context is available
const ctx = getAudioContext();
// Create our own analyser
const analyser = ctx.createAnalyser();
analyser.fftSize = 512;
// We need to intercept the output - Strudel connects to ctx.destination
// Option: Create a custom destination wrapper
- Use Strudel's built-in analysers:
import { getAnalyserById, getAnalyzerData } from '@strudel/superdough';
// Get analyser (creates if needed)
const analyser = getAnalyserById(1, 512, 0.5);
// In animation loop:
const freqData = getAnalyzerData('frequency', 1);
const timeData = getAnalyzerData('time', 1);
Issue: Strudel's analysers are per-orbit, not global output. Need to investigate how to connect to global output.
Recommended Integration Approach #
- Use
@strudel/webfor headless control - Intercept audio at the destination:
// After initStrudel()
const ctx = getAudioContext();
// Create a gain node to intercept ALL audio
const masterGain = ctx.createGain();
const analyser = ctx.createAnalyser();
analyser.fftSize = 512;
// Monkey-patch or use custom AudioContext setup
// to route all Strudel output through our analyser
masterGain.connect(analyser);
analyser.connect(ctx.destination);
- Alternative: Modify superdoughoutput.mjs
In SuperdoughOutput.initializeAudio():
this.destinationGain.connect(audioContext.destination);
// Add: this.destinationGain.connect(window.strudelAnalyser);
Jukebox Implementation Plan #
Phase 1: Basic Strudel Playback #
- Add Strudel tab to Jukebox
- Load
@strudel/webdynamically - Provide code editor for Strudel patterns
- Play/Stop buttons call
evaluate()andhush()
Phase 2: Audio Analysis #
- Create custom AudioContext wrapper that includes our analyser
- Call
setAudioContext(customCtx)beforeinitStrudel() - Route Strudel output through our analyser
- Send analysis data to KidLisp (
amp,bass,mid,treble,kick)
Phase 3: KidLisp Integration #
- Strudel patterns can be stored alongside KidLisp code
(strudel "s('bd sd')")function in KidLisp- Live audio variables react to Strudel playback
Sample Code: Custom Audio Context with Analysis #
// Create our AudioContext with analyser in the chain
const audioContext = new AudioContext();
const masterGain = audioContext.createGain();
const analyser = audioContext.createAnalyser();
analyser.fftSize = 512;
// Create a proxy destination
const proxyDestination = masterGain;
masterGain.connect(analyser);
analyser.connect(audioContext.destination);
// Set up Strudel with our context
import { setAudioContext } from '@strudel/superdough';
setAudioContext(audioContext);
// Now initStrudel will use our context
initStrudel();
// Analysis loop
function analyze() {
const freqData = new Uint8Array(analyser.frequencyBinCount);
const timeData = new Uint8Array(analyser.fftSize);
analyser.getByteFrequencyData(freqData);
analyser.getByteTimeDomainData(timeData);
// Calculate amp, bass, mid, treble, kick...
// Send to KidLisp
requestAnimationFrame(analyze);
}
License Considerations #
Strudel uses AGPL-3.0:
- Source code must be made available for network services
- Derivative works must use same license
- Our use is embedding/integration, not modification
- If we modify Strudel code, must share those changes
- aesthetic.computer is already open source, so this is fine
Dependencies #
@strudel/web (1.3.0) bundles:
- @strudel/core - Pattern engine
- @strudel/mini - Mini-notation parser
- @strudel/tonal - Music theory
- @strudel/webaudio - WebAudio integration
- @strudel/transpiler - Code transformation
- superdough - Synthesis engine
Total unpacked size: ~1.55 MB
Next Steps #
- Create proof-of-concept in kidlisp.com with @strudel/web
- Test audio interception approach
- Implement pattern editor in Jukebox tab
- Connect analysis to KidLisp audio variables
- Add preset patterns for quick testing