Code
Ctrl/Cmd+Shift+Space/I · Ctrl/Cmd+Space/IChanges are saved to this virtual TFI file automatically. Pan, LFO, AMS/PMS, AM and CH3 settings are for audition only; TFI does not store them.
Press Run to observe audio.
Run code containing liveFx to begin.
Tetorica Shell — type help for commands.
Shell
- Enter
- Execute
- ↑ / ↓
- History
- Tab
- Complete path
- Ctrl+C
- Stop script
- Ctrl+L
- Clear output
Try js /scripts/example.js hello or play /index.js. Type help for commands.
Stop Script ends the shell script. Music stops with the toolbar STOP.
Generating Playground code with an AI? Open For AI documentation for type definitions and working examples.
Sample Editor and Keyboard
- Open Menu → Sample Editor… to record, load, trim or draw a sound. The original WAV and edit settings are saved together in your Cassette.
- Run your mode's
*-sampler.jsexample in FILES, then open Keyboard to play the edited sound with the Guitar layout. const audio = await inputAudio({asset:"/samples/sampler.sample.json", edit:false})reuses a saved sound; it opens the editor if the asset is missing. Cancel returnsnull.keyboard.bind("sampler", await createSampleInstrument(audio))connects the sound. Custom targets providenoteOn({id,midi,velocity}),noteOff(event)andallNotesOff().
pg
pgis the main playground helper namespacepg.play("C4", { channel: CH1, duration: 0.2 })plays one notepg.sleep(0.25)waits in secondssleepSamples(735)waits in VGM-style 44.1kHz sample unitspg.liveLoop("bass", async () => {})keeps a named loop alivepg.setBpm(120),await pg.beat(1),await pg.nextBeat(),await pg.tween(1, (t) => {})pg.setMasterVolume(1.5),pg.getMasterVolume()await pg.livePrepare("main-fx", async ({ fx }) => ...)reuses prepared live state across runspg.scale("Eb2", "majorPentatonic", 2),pg.chord("B2", "minor")pg.lerp(0, 100, 0.5)interpolates between two numberspg.noteToBlockFnum("C4"),pg.noteLerp("B2", "E3", 0.5)convert FM pitchhzToBlockFnum(440)/pg.hzToBlockFnum(440)converts Hz to{block, fnum}forfm.setFrequency(channel, block, fnum). Optional second argument: chip clock in Hz (default 7670454), not the audio sample rate.pg.choose(array),pg.cycle(array),pg.cycle("bass", array)pg.rand(),pg.rrange(0.2, 0.8),pg.randInt(0, 7)pg.stopLoop(name),pg.stopAllLoops(),pg.stopAll()
Go to Definition (F12 / Ctrl or Cmd + click) opens library declarations read-only. View implementation opens the corresponding JavaScript source.
Game Boy
- Open the Game Boy tab to inspect or edit the current voice and draw Wave RAM. Prepare audio works without Run; use Hold to play or Use Keyboard to audition.
gameboyis the primary Game Boy APU, also available throughpg.gameboyandlivePrepare(({gameboy}) => ...). It is initialized when audio starts.gameboy.pulse.setVoice(0, {duty: 0.25, volume: 9}),gameboy.pulse.noteOn(0, "C4"),gameboy.pulse.keyOff(0). Pulse channels are0and1.gameboy.wave.stopAndSetWaveform(samples)loads 32 four-bit samples. ChoosesetLevel(0.5), thennoteOn("C3")/keyOff().gameboy.noise.setVoice({volume: 8, divisor: 3, shift: 4, width: 15}),gameboy.noise.keyOn(),gameboy.noise.keyOff().gameboy.setPan(channel, left, right)routes APU channels0..3. Time notes withbeat()/sleep()and useliveLoop()for repeated patterns. Seeexamples/gameboy/gameboy-mode-pulse.js.
nes.pulse, nes.triangle, nes.noise, nes.dmc and nes.fds control the primary NES + FDS. Use nes.pulse.noteOn(0, "C4"), await beat(0.5), then nes.pulse.keyOff(0).
Open the NES + FDS tab for voice settings and FDS waveform editing. Prepare audio works without Run; Hold to play previews the selected channel and Use Keyboard connects the Guitar. DMC requires a loaded sample.
Saturn SCSP has 32 PCM slots and 512 KiB RAM. Prepare audio in the SCSP tab, then use Hold to play or Keyboard. PCM / envelope edits currently use JavaScript. Experimental Web Audio mode; no SSF player.
PlayStation SPU has 24 sample voices and 512 KiB RAM. An original waveform is ready for Keyboard after Prepare audio. Use spu.noteOn(0, "C4") / spu.keyOff(0). Open SPU to choose a sample, voice, stereo gain and envelope. Experimental Web Audio mode.
snes.loadBRR(bytes, {rootNote:"C4", loopStartSamples:0}) prepares a BRR instrument. Assign it with snes.setPreset(0, {sample:tone}), then use snes.noteOn(0, "C4") / snes.keyOff(0). Channels are 0..7.
opm controls the eight YM2151 FM channels. adpcm controls the OKIM6258: await adpcm.loadSample({channels:[pcm], sampleRate:15625}), then adpcm.play() / adpcm.stop().
Open OKIM6258 for the loaded ADPCM sample, decoded waveform, divider and stereo routing. Load an audio file from the Cassette, then use Hold to play or Use Keyboard to trigger it. Every Keyboard key plays the same sample at the selected rate.
Open YM2151 for channel and operator settings. Prepare audio works without Run; Hold to play previews a sound and Use Keyboard connects the Guitar. The operator display uses logical M1 / M2 / C1 / C2 order.
Open HuC6280 to view and draw 32-sample, five-bit waveforms, adjust each channel’s volume and stereo balance, and try the sound with Hold to play or the shared Guitar Keyboard. Prepare audio works without Run. Noise is on CH5/CH6; LFO uses CH2 to modulate CH1. DDA is direct sample output, so switch to wavetable mode for note audition.
Open WonderSwan to draw 32-sample, four-bit waveforms and adjust four channels’ left/right volume. Try the sound with Hold to play or the shared Guitar Keyboard. CH2 supports Voice PCM, CH3 Sweep and CH4 Noise. ws.noteOn(0, "C4") plays a channel; ws.keyOff(0) releases it.
sid is a CPU-free 6581 approximation with three physical voices (0..2) and one shared filter. Set a voice with sid.setVoice(0, {waveform:"saw", attack:2, decay:6, sustain:12, release:5}), then sid.noteOn(0, "C4") / sid.keyOff(0). Open SID to inspect gates, voice settings, filter routing and the authored register shadow. Edit in Code; panel audition, 8580 and .sid song playback are not available in this prototype.
pokey is the first four-channel POKEY. Use pokey.setVolume(0, 6), pokey.setNote(0, "C4"), pokey.keyOn(0) and pokey.keyOff(0). Channels are 0..3; distortion 5 is a pure tone. Atari Arcade mode also provides pokey2, with its own four channels. Keyboard selects POKEY 1 or POKEY 2 and a physical channel. Each chip is mono; Mixer Pan controls host stereo placement.
Open POKEY to select a chip and physical channel, inspect applied registers, and edit volume, distortion, volume-only mode and shared clocks. Prepare audio works without Run; Hold to play or the shared Guitar Keyboard auditions the selected voice. Joined pairs use the upper channel for sound and the lower channel as a timer helper. Shared clock changes affect other voices on the same chip.
pce.setWaveform(0, wave) loads 32 samples in 0..31. Use pce.setVolume(0, 20), pce.noteOn(0, "C4") and pce.keyOff(0). Channels are 0..5; noise is available on 4 and 5.
opl is the primary OPL2 instrument. Use opl.setPreset(0, OPL_PRESETS.bell), opl.noteOn(0, "C4") and opl.keyOff(0). Channels are numbered 0..8; MIDI note 69 is A4. Open YM3812 to inspect and edit operators, load an original preset and audition the current voice. Keyboard selects the chip and physical channel.
opl is the primary OPL instrument. Use opl.setPreset(0, OPL_PRESETS.bell), opl.noteOn(0, "C4") and opl.keyOff(0). Channels are numbered 0..8; MIDI note 69 is A4. Open YM3526 to inspect and edit operators, load an original preset and audition the current voice. Keyboard selects the chip and physical channel.
opl is the primary Y8950 FM instrument. Use opl.setPreset(0, OPL_PRESETS.bell), opl.noteOn(0, "C4") and opl.keyOff(0). Channels are numbered 0..8; MIDI note 69 is A4. Open Y8950 to inspect and edit operators, load an original preset and audition the current voice. Keyboard selects the chip and physical channel. Y8950 ADPCM is not part of this FM mode.
opl is the primary OPL3 instrument. Use opl.setPreset(0, OPL_PRESETS.bell), opl.noteOn(0, "C4") and opl.keyOff(0). Channels are numbered 0..17; MIDI note 69 is A4. Open YMF262 to inspect and edit operators, load an original preset and audition the current voice. Keyboard selects the chip and physical channel. Four-op primaries are 0..2 and 9..11; each borrows its primary + 3. Keyboard excludes paired secondary channels. Stereo uses left/right routing switches.
Atari ST provides ay for its mono YM2149 PSG at 2 MHz. ay.setVoice(0, {tone:true, noise:false, volume:9}), ay.noteOn(0, "C4") and ay.keyOff(0) configure and play a voice. Channels are 0..2. await useSoundChip("ay8910") borrows this same PSG.
Open YM2149 to edit tone, noise, fixed volume and the shared hardware envelope, then audition the selected voice with Hold to play or Keyboard. Three voices share one noise generator and one envelope; writing its Shape retriggers it globally. This mode models the original ST PSG, without STE DMA audio.
MSX provides ay for PSG and opll for MSX-MUSIC. The Keyboard selects each chip and its physical channel. Open its chip tab to edit and audition the current voice.
ay.setVoice(0, {tone:true, noise:false, volume:9}) configures a PSG voice. ay.noteOn(0, "C4") and ay.keyOff(0) play it. Three channels share one noise generator and one hardware envelope; writing its Shape explicitly retriggers it globally.
opll.setVoice(0, {instrument:3, attenuation:5}) selects a ROM instrument; instrument 0 uses the shared OPLL_USER_PRESETS patch. Use opll.noteOn(0, "C4") and opll.keyOff(0) to play it. Select MSX Audio for the additional Y8950 FM instrument.
MSX Audio provides ay for PSG, opll for MSX-MUSIC and opl for MSX-AUDIO FM. The Keyboard selects each chip and its physical channel. Open its chip tab to edit and audition the current voice.
ay.setVoice(0, {tone:true, noise:false, volume:9}) configures a PSG voice. Three channels share its noise generator and hardware envelope. opll.setVoice(0, {instrument:3, attenuation:5}) selects a YM2413 ROM voice; instrument 0 uses the shared custom patch.
opl.setPreset(0, OPL_PRESETS.bell), opl.noteOn(0, "C4") and opl.keyOff(0) play Y8950 FM. Its tab edits operators and rhythm. Use await opl.adpcm.loadSample(pcm) for a mono sample, then opl.adpcm.setVolume(160), opl.adpcm.keyOn() and opl.adpcm.keyOff(). See examples/msx/msx-audio-adpcm.js.
dx7 is the primary 16-voice MSFA instrument. Use dx7.noteOn(60, 100) and dx7.noteOff(60) with MIDI note numbers and no channel argument. await useSoundChip("dx7") borrows this same instrument; await createSoundChip("dx7") creates another one.
Use object presets with await dx7.setPreset(preset), or import a SysEx file and await dx7.loadSysex(await file("/presets/my-bank.syx", {type:"arrayBuffer"})). Open the DX7 panel to inspect and edit all six operators, select a bank voice and audition it. The Keyboard tab plays this instrument directly; keyboard.bind() can supply another instrument. The engine runs internally at 44,100 Hz; there is no chip-clock setting.
midi (YM2612 mode)
midi.output("tetorica-ym2612", { channel: CH1 })creates an FM part;"tetorica-sega-psg"uses the three PSG tone voices (not noise). These are internal outputs, not external Web MIDI ports.channelis a MIDI part: numeric0..15orCH1..CH16. Omitted channel defaults to CH1. By default the engine shares six FM voices or three PSG tone voices automatically; a MIDI channel does not pin a physical voice.await midi.enableSoundChip("tetorica-ym2612", { roundRobin: false })selects fixed FM voices: CH1..CH6 map to physical CH1..CH6, one note each; CH7..CH16 do not sound.roundRobin: truerestores automatic allocation. Changing mode stops FM notes. Specify the mode at the start of your script; this option is YM2612-only.await lead.setVoice(FM_PRESETS["two-op-bell"])selects an FM voice;await lead.loadVoice("./lead.tfi")loads TFI or VGI from FILES. With channel omitted,setVoice()applies to all MIDI channels.await lead.noteOn("C4", { velocity: 100 })andawait lead.noteOff("C4")start and release a note. Notes also accept MIDI numbers0..127.await lead.play("C4", { velocity: 100, duration: 1 })plays for one beat at the current BPM. UsePromise.all([...])for chords.await lead.pitchBend(0.25)bends the part (-1..1, center0);await lead.setPitchBendRange(2)sets the range in semitones (default ±2). Bend affects notes belonging to that MIDI part.await lead.cc(number, value)sends a MIDI controller, values0..127:7volume,10pan (FM left/center/right; PSG ignores pan),11expression,64sustain (64 or higher = down),120all sound off,121reset controllers,123all notes off.- Import MIDI generates editable JavaScript using these APIs. The example list includes MIDI fixed CH liveLoop + CC / Bend and MIDI auto chord liveLoop + CC / Bend.
Live coding
- Top-level
awaitis available; pressRunagain to replace named loops context(alsopg.context) keeps values between runs untilStoplivePrepare("name", setup)creates a resource once and reuses it on later runsliveCleanup(["bass", "drums"], cleanup)runs cleanup after those loops disappearonKeyboardPressKey("play-c", event => ...)andonKeyboardReleaseKey(...)register replaceable keyboard handlers
Files, imports, and console
- Use the
FILESbuttons to create, import, rename, and delete project files await file("./notes.txt"),await file("./data.json", { type: "json" }),await file("./sound.wav", { type: "arrayBuffer" })read project files relative to the running fileconst { bass } = await import("./lib/bass.js")dynamically imports a project JavaScript module- Imported modules may use
exportand nested relativeimport(); use dynamicimport()instead of a top-level staticimport liveFx(name, {context, process(input, output, state, context), resetState?})registers JavaScript FX after native FX. Same-name Apply preserves state; Stop removes it. Input/output are arrays of channel Float32Arrays. The synchronous process runs in AudioWorklet: use context instead of captured variables, and avoid unbounded loops. Exceptions/non-finite output bypass that effect.fx.updateContext(name, patch)sends a context update;fx.removeLiveFx(name)removes it. Commands are queued, not sample-timestamped. Seeexamples/basic/livefx/live-fx-distortion.js.log(value),console.log(value),console.warn(value), andconsole.error(value)write to the Console tabtfiToPreset(await file("./bass.tfi", { type: "arrayBuffer" }))converts a 42-byte TFI file forfm.setPreset()vgiToPreset(await file("./bass.vgi", { type: "arrayBuffer" }))converts a 43-byte VGI file forfm.setPreset()(binary files requiretype: "arrayBuffer")
Sound chips
- In PC-98 (YM2203 / OPN) mode,
fmandssgcontrol the same default YM2203. SSG offers three tone/noise/envelope channels in Main and Worker, also throughpg.ssgandlivePrepare(). Seeexamples/pc98/pc98-opn-ssg.js. - In PC-98 (YM2608 / OPNA) mode,
fm,ssg,rhythmandadpcmcontrol the same default YM2608 in Main and Worker. Also available throughpgandlivePrepare(). Seeexamples/pc98/pc98-opna-sections.js. - Neo Geo (YM2610, four FM channels) and Neo Geo (YM2610B, six FM channels) expose
ssg(tone/noise/envelope),adpcmA(six voices) andadpcmBon the default chip.adpcmaliasesadpcmB. Main, Worker,pgandlivePrepare()share these APIs. Upload encoded A/B data withawait loadMemory(); seeexamples/neogeo/neogeo-opnb-sections.js. const chip = await useSoundChip(name)reuses a chip until Stop. For YM2612, YM2203, and YM2610, it returns the default chip when selected; otherwise it creates an independent chip.const chip = await createSoundChip(name)creates a fresh independent chip routed through master FX.createSoundChip("nes", {fds: true})enables FDS. Usechip.pulse,chip.triangle,chip.noise,chip.dmc, andchip.fds; seeexamples/nes/.- Supported names:
sid(Web Audio prototype),ym2612,ym2203,ym2610,ym2608,rf5c164,gameboy,segapsg,ym2151,pwm,nes,dx7,snes,ym3526,ym3812,y8950,ymf262,ym2413, anday8910. const snes = await useSoundChip("snes")creates an independent, CPU-free S-DSP with eight voices (0..7). Load BRR with explicit tuning, then callsnes.setPreset(0, {sample})andsnes.noteOn(0, "C4"). Native audio is 32,000 Hz; there is nosetClock(). Seeexamples/snes/snes-brr-melody.js.const dx7 = await useSoundChip("dx7")creates a 16-voice MSFA DX7 with the self-authored INIT SINE voice. Usedx7.noteOn(60, 100)anddx7.noteOff(60)with MIDI note numbers, without a channel argument.- Import a DX7 SysEx file into FILES, then
await dx7.loadSysex(await file("/presets/my-bank.syx", {type:"arrayBuffer"})). Choosedx7.selectVoice(0)fromdx7.voices. The internal rate is fixed at 44,100 Hz; there is nosetClock(). Seeexamples/dx7/dx7-init-sine.js. - Control additional chips through the returned object. Stop disposes additional chips.
- Global
play()andwrite()target the default FM chip. - Concurrent
useSoundChip()requests share initialization. Options other than an empty object are not supported.
Chip editors and Mixer
- Sega PSG, RF5C164 and 32X PWM tabs edit connected chips. Choose an instance at the top; opening a tab does not create or reset a chip.
- Open the lower Mixer panel to adjust each chip's Volume, Pan, Mute and Solo. Multiple Solo buttons can be active; clearing Solo preserves manual Mute.
await mixer.set(chip.id, {volume: 0.5, pan: -0.25})adjusts the same output as the UI. Volume is 0–2; Pan is −1–1.await mixer.set(chip.id, {solo: true})solos a chip.- Master mirrors the Vol slider. A script's
setMasterVolume()remains a separate gain. Mixer settings survive Stop → Run for the default chips and cacheduseSoundChip()instances; explicit code changes still apply. - RF5C164 loads audio from FILES into free sample RAM. Choose Start or Loop to drag a waveform marker. 32X PWM exposes routing and FIFO state; it does not load sample files.
- See
examples/genesis/mixer-chip-balance.js. Chip polling and meter analysis pause while their panels or the page are hidden.
YM2151 (OPM)
const opm = await useSoundChip("ym2151")reuses an independent eight-channel FM chip;createSoundChip("ym2151")creates a fresh instance.opm.setAlgo(0, 7)selects an algorithm;opm.setPan(0, true, true)enables both outputs. Channels are numeric0..7.opm.setOperator(0, 3, { mul: 1, tl: 32, ar: 31, rr: 15 })edits an operator. Indices0..3follow register orderM1, C1, M2, C2; these are OPM parameters, not YM2612 presets.opm.setNote(0, "A4")sets pitch;opm.keyOn(0)starts the envelope;opm.keyOff(0)releases it. Notes acceptC#0..C8or MIDI integers13..108.opm.setPitch(0, 0x4a, 0)sets raw key code and key fraction (0..63). The clock is fixed at3579545 Hz.opm.setNoise(true, 12)replaces CH8's last operator with noise (rate0..31);opm.writeRegister(register, value)accesses all registers, including LFO.opm.reset()resets this chip;opm.dispose()releases it. Openexamples/x68000/ym2151-tone.jsfor a complete voice setup.
Game Gear / Master System PSG
psgis the primary sound chip, also available throughpg.psgandlivePrepare(). Tone channels arePSG1,PSG2, andPSG3.psg.tone(PSG1, {note:"C4", attenuation:6})starts a tone;psg.off(PSG1)stops it. Useawait beat(...)to set its duration.psg.noise({type:"white", rate:"medium", attenuation:10})starts noise;psg.noiseOff()stops it.await psg.setClock(hz)changes the PSG clock.await useSoundChip("segapsg")returns this primary chip;createSoundChip("segapsg")adds an independent chip. Stop silences the primary tones and noise.opllis the built-in YM2413, separate from the PSG. Useopll.setVoice(0, {instrument:3, attenuation:5}),opll.noteOn(0, "C4")andopll.keyOff(0). YM2413 shows instruments, shared User patch editing and audition. Keyboard selects Sega PSG or YM2413 and its channel.- Instruments 1–15 are built-in ROM voices. Instrument 0 uses one shared User patch:
opll.setUserPatch(OPLL_USER_PRESETS.bell)changes every channel using instrument 0. Rhythm mode provides six melodic channels and five percussion voices. psg.setStereo(0xff)enables all voices on both sides. Bits 4–7 enable left Tone 1/2/3/Noise; bits 0–3 enable the corresponding right voices. For Tone 1,0xfeis left only and0xefis right only.
Sega PSG (independent chip)
const chip = await useSoundChip("segapsg")reuses an independent Sega PSG;createSoundChip("segapsg")creates a fresh instance.chip.tone(0, { note: "C4", attenuation: 4 })starts a tone on channel0..2;chip.off(0)silences it.chip.noise({ type: "white", rate: "medium", attenuation: 8 })starts noise;chip.noiseOff()silences it.- Uses the same API as
psg. Bothreset()andresetAll()reset only this instance.dispose()releases it; Stop releases all additional chips. - Open
examples/genesis/segapsg-tone-noise.jsin FILES for an example.
YM2608 ADPCM-B sample loading
YM2608 ADPCM-B can also load decoded PCM or PCM/Float WAV with
await ym2608.adpcm.loadSample(source, {address: 0}).
It converts the sample, uploads memory and selects the range/rate; use
setVolume(), setPan() and keyOn() to play.
Open examples/pc98/ym2608-adpcm-load-sample.js in FILES.
RF5C164 PCM
const pcm = await createSoundChip("rf5c164")creates an eight-channel PCM chip. Physical channels areCH1..CH8.await pcm.loadMemory(bytes, address)loads raw RF5C164 BIN bytes from aUint8ArrayorArrayBuffer.await pcm.loadSample(source, { address, loopStart })decodes WAV/FLAC and returnsstart,loopStart, andstepforsetChannel(). Format support depends on the browser.- Sample sources may be a local URL, Blob, or encoded file bytes. Read FILES data with
await file("./sound.wav", { type: "arrayBuffer" })first. Converted samples must fit 64 KiB; no automatic resampling. await pcm.setChannel(ch, { start, loopStart, step, volume, pan: { left, right } })configures a channel. Start is 256-byte aligned; volume is0..255, pan values0..15.await pcm.setPitch(ch, step)changes pitch;await pcm.keyOn(ch)starts or retriggers playback;await pcm.keyOff(ch)stops it.pcm.reset()retains sample RAM;pcm.dispose()releases the chip.- Open
examples/genesis/rf5c164-sine.jsin FILES for an example.
dac (YM2612 mode)
- DAC plays unsigned 8-bit PCM through physical CH6, replacing its FM output while enabled. Values are
0..255;128is the neutral level. fm.setPan(CH6, true, true)routes CH6 to both speakers.fm.setDacEnabled(true)enables DAC;fm.writeDac(128)writes one value;fm.setDacEnabled(false)restores FM output.const start = beginSampleSchedule()gets the current loop cycle start in 44,100 Hz sample units.dac.schedule(start, entries)queues[offsetSamples, value]pairs relative to that start. Use/** @type {Array<[number, number]>} */for an initially empty entries array.- Offsets use
44,100 units = 1 second, regardless of the audio device sample rate. One DAC value every 4 units gives an 11,025 Hz PCM stream. Use audio-clock scheduling rather thansetInterval()for steady playback. scheduleWritesSamples(start, entries)queues[offsetSamples, port, register, value]writes. Use it to time DAC enable/disable along with PCM.await sleepSamples(length)waits in the same 44,100 Hz units.await dac.load(name, data)loads packed records from aUint8ArrayorArrayBuffer: each 5-byte record contains a little-endian uint32 sample offset followed by one uint8 DAC value. This is not a WAV file or a raw PCM byte array.dac.playStream(name, { atSamples: start })schedules a loaded bank relative to the run's sample-clock origin; it does not wait for playback to finish. Enable DAC and set CH6 pan separately.await dac.loadBase64(name, encoded)loads the same packed format from Base64;dac.scheduleBase64(start, encoded)schedules packed records directly.- Open
examples/basic/dac/dac-byte-stream.jsin FILES for the 440 Hz sine-wave example. For ordinary decoded audio playback, usesamplebelow.
sample and noise
await sample.load("sonic-pi/ambi-choir")loads a built-in sampleawait sample.loadFile("/samples/hit.wav")loads audio from the projectawait sample.play("name", { gain: 0.8, playbackRate: 0.5, pan: -0.5, loop: true, fadeIn: 0.1 })sample.stop("name"),sample.stopAll(),sample.list()noise.create({ type: "pink", gain: 0.1, pan: 0 })creates a controllable noise voice; types arewhite,pink,brown,gray, andclipcontrol(voice, { gain: 0.2, cutoff: 1800, q: 0.8, slide: 1 })changes a noise voice smoothlyvoice.stop()stops one voice;noise.stopAll()stops every noise voice
stream
await stream.load("bgm", "./bgm.mp3")prepares a browser media stream from a same-origin URL; suitable for long audio such as background music. Use an actual served URL, not a FILES virtual path.await stream.play("bgm", { gain: 0.5, loop: true })waits for playback to start, not for the track to finish.- Sample reverse playback:
await sample.play("name", { playbackRate: -1 }). Use -0.5 for half-speed reverse; zero is invalid. For reverse playback, offset counts seconds from the end. Loop bounds remain relative to the original file start. - Playback options include
playbackRate,offset(seconds),pan, andfadeIn(seconds). stream.pause("bgm")pauses;stream.play("bgm")resumes without an explicit offset.stream.stop("bgm")stops and rewinds.stream.unload("bgm")releases the entry;stream.list()andstream.isLoaded("bgm")inspect loaded entries.- Output passes through the master FX by default. Playback controls remain on the main thread, including in Worker mode. This API is separate from
dac.playStream().
fm
fmcontrols the selected mode's primary FM chipawait play("C4")plays one notepg.fmpoints to the same primary FM chip- FM channels: 3 on YM2203, 4 on Neo Geo YM2610, 6 on YM2612/YM2608. Logical operators are
OP1throughOP4. fm.setPreset(CH1, pg.presets["one-op-basic"])applies one presetfm.setOperators(CH1, [[OP1, { tl: 20 }], [OP3, { tl: 24 }]])applies partial settings in array order (YM2612). Repeated operators are allowed. Fields within each entry followsetOperator()'s register order.fm.setOperator(CH1, OP1, { multi: 1, tl: 20, ar: 24, d1r: 8, d2r: 4, sl: 6, rr: 8 })- Operator ranges:
multi 0..15,tl 0..127(lower is louder),ar/d1r/d2r 0..31,sl/rr 0..15 fm.setAlgo(CH1, 7, 0)sets algorithm and feedback (0..7)fm.setPan(CH1, true, true, 0, 0)sets left, right, AMS (0..3), and PMS (0..7)fm.setFrequency(CH1, 4, 553),fm.keyOn(CH1),fm.keyOff(CH1)expose the YM2612 note trigger flowwrite(register, value)sends one YM2612 register write on port 0write(port, register, value)sends one YM2612 register write with an explicit portfm.write(port, register, value)sends one YM2612 register writefm.writeAddress(port, register),fm.writeData(value)follow the YM2612 address/data flowfm.read(offset),fm.readStatus(),fm.getIrq()expose low-level chip state where available
psg
psgis the raw Sega PSG (SN76489-compatible) layer, mixed into the same output asfmpg.psgpoints to the same PSG layerpg.psgTone(channel, period, attenuation)writes one PSG tone note helper for channel0..2pg.psgNoise(mode, attenuation)writes one PSG noise helper using raw mode0..7psg.tone(PSG1, { note: "C4", volume: 0.5 }); channels arePSG1,PSG2, andPSG3psg.noise({ type: "white", rate: "medium", volume: 0.25 }); rates arelow,medium,high, andtone3psg.off(PSG1)andpsg.noiseOff()silence tone and noise channelspsg.write(value)sends one raw PSG register byte (tone/noise/attenuation, same format asdocs/demos/psg.html)psg.reset()resets only PSGpsg.resetAll()resets both PSG and YM2612
fx
fxis the master FX helper layerpg.fxpoints to the same FX helper layerfx.gain(...),fx.eq(...),fx.filter(...),fx.delay(...),fx.distortion(...),fx.compressor(...),fx.gate(...)fx.bitcrusher(...),fx.wobble(...),fx.flanger(...),fx.chorus(...),fx.reverb(...),fx.slicer(...)- FX run in native C/WASM. Each type supports up to 8 instances; the routing graph supports up to 32 nodes. Modulation rate / phase is in beats. EQ uses fixed 200 Hz / 1 kHz / 4 kHz bands.
fx.setChain([filter, delay])replaces the master FX chainfx.setChain([fx.parallel(fx.branch(filter), fx.branch(delay, reverb))])creates parallel branchesfx.clear()detaches the current master FX chain and clears its audio historyeq.bass.set(...),eq.mid.rampTo(...),eq.treble.set(...)filter.cutoff.set(...),filter.cutoff.rampTo(...)
Note
Names passed to liveLoop(), livePrepare(), and
keyboard handlers identify replaceable live state. Reuse the same
name when editing a part, and use different names for independent parts.
Shared Playground URLs may include JavaScript source via ?src=.
That source is loaded into the editor and waits for an explicit
Run. During execution, common network APIs are disabled.
This is a small safety guard, not a full security sandbox.
- Choose examples from the
examples/folders in FILES, then press Run. ?src=...loads base64-encoded JavaScript source into the editor?cassette=...loads a small Base64URL-encoded cassette zip?tfi=...&tfi-id=bassloads one URL TFI preset with idbass?tfi=...,...with?tfi-id=a,bloads multiple URL TFI presets?vgi=...&vgi-id=bassloads one URL VGI preset with idbass