ZzFXTrack is a music creation app that uses ZzFXMicro synthesis by Frank Force. It embeds Strudel for its REPL workflow and adds custom-made Arranger and Blocks tools with a tracker-style interface. The Arranger and Blocks are built on Strudel's patterns and scheduling, with one-way integration.
Instruments can be created using a streamlined editor with scrub inputs. Instrument playback is handled externally by ZzFXMicro instead of Strudel's internal sound engine. This keeps exported music optimal and predictable for games and demos.
Exported music can be played with the bundled ZzFXTrack Player, which takes inspiration from ZzFXM by Keith Clark and Frank Force. Developers can freely use it in their projects or build their own player. WAV export is also supported.
Disclaimer: ZzFXTrack is not affiliated with or endorsed by the creators of Strudel, ZzFX (Frank Force), or ZzFXM (Keith Clark & Frank Force). It is not intended for small game compos. It was originally made for my personal projects, but I'm open to expanding the idea and collaborating.
- Open an Arrangement or Pattern from the left sidebar and hit play.
- Select an Instrument from the left sidebar that is being used in the playback.
- Make some changes to the parameters to see how the sound changes.
- After that, try modifying Blocks, Arrangements, or the Strudel pattern.
- You can then click Export in the footer and choose ZzFXTrack Player.
- Next you can click the play button to preview ZzFXTrack Player playback.
- The sound you hear now comes from the exported JSON file and ZzFXTrack Player.
- Click
Export WAVif you want a quick audio file to share or test.
Take your time and explore. There are many features to discover beyond what's explained directly.
The hosted version is the quickest way to start (no install required), especially if you just want to write patterns, tweak instruments, and export. Local setup is mainly for developers who want to modify the app/exporter or run it offline.
The hosted demo is intended to run on GitHub Pages at https://jkooler.github.io/zzfxtrack/.
It is deployed as a static demo build, so file-backed editing APIs are not available there. Patterns, arrangements, and blocks behave like demo content and persist only in browser storage for that session unless exported.
- In GitHub, open
Settings > Pages. - Set
SourcetoGitHub Actions. - Push to the default branch (
masterin this repo) or run theDeploy to GitHub Pagesworkflow manually.
The workflow builds the hosted demo bundle and publishes dist/ to Pages automatically.
- Node.js:
^20.19.0 || >=22.12.0(matches Vite's engine requirement inpackage-lock.json) - npm (any version that supports lockfile v3 is fine)
- Compose music using a Strudel REPL workflow (patterns + scheduling).
- Design instruments using ZzFXMicro-style parameters (and use those same instruments during Strudel playback).
- Export JSON song data for use in games/apps that use ZzFXTrack Player song playback.
Disclaimer: This app was originally built for my personal projects, but it’s designed so others can compose, export, and embed the player as well.
This is an alpha:
- The exporter is the “product”. The Strudel REPL is the authoring UI. The Visual Arranger is for those who prefer a more traditional workflow.
- Some Strudel features may preview fine but not export 1:1 (the README includes export-safe tips below).
- If something exports incorrectly, please open an issue with a minimal pattern reproduction.
- Intended to remain reasonably stable across upcoming releases:
- Export: JSON/JS structure for the ZzFXTrack Player
- Instrument parameters
- Resources (Patterns, Arrangements, Blocks, Tracker)
- Subject to change:
- UI/UX details
- Install dependencies:
npm install- Start the app:
npm run dev- Export patterns:
npm run export -- 01-Introduction
npm run export -- --all
npm run export -- --all --combinedThis app is optimized for creating ZzFXTrack Player-ready music with Strudel patterns. Strudel itself can do much more than what is practical to export 1:1, so it helps to stay within an "export-safe" subset.
- Use the
Balancedplayback loudness preset as a good default starting point. - If you hear clipping/distortion with dense chords or many layers, reduce loudness (or enable soft clipping) rather than pushing everything hotter.
The export format is effectively monophonic per channel. If you use chords, export may expand voices into more channels.
- If you target tight constraints (js13k-style), consider enabling
Limit channel usage. - If you use stacked unison notes, consider enabling
Normalize Layers.
Export uses rows per cycle (e.g. 48 or 96) to grid notes in time. Each cycle (e.g. one bar) is that many rows in the pattern data.
- Higher resolution (e.g. 96) → more rows per bar → larger JSON and more precise timing.
- Lower resolution (e.g. 48) → fewer rows per bar → smaller export and slightly coarser timing.
If exported pattern data feels long or bloated, try a lower resolution; it often reduces file size noticeably with little audible difference for typical loops.
//! Generated by ZzFXTrack
// ZzFXTrack Player song data: [instruments, patterns, sequence, BPM]
export const songData = {"song":[[[0.2,0,232,0.01,0,0,4,1,0,0,0,0,0,0,0,0,0,0,1,0,0],[0.9,0,72,0,0.01,0.08,0,1,0,0,0,0,0,0,0,0,0,0.57,0.01,0,0],[1.3,0.05,233.08,0,0.16,0,1,14,38,98,358,0,0,2.16,730,0,0,0.06,0.17,0,578],[0.85,0,440,0,0.29,0.16,1,6.3,0,0,0,0,0.48,0.15,0,0,0,0.04,0.1,-0.5,0],[0.5,0,440,0.5,0.21,0.92,2,3,0,0,0,0,0.4,0.2,0,0,0,0.2,0.4,0.3,-553],[0.25,0,440,0.01,0,0,2,1.5,0,0,0,0,0,0,0,0,0,0,1,0,0]],[[{"72":[0,16,2.0805446207650533],"_":96},{"0":[1,0,11.337216544988166],"12":[1,0,-8.662783455011834],"24":[1,0,18.337216544988166],"36":[1,0,53.337216544988166],"48":[1,0,-0.6627834550118337],"54":[1,0,12.337216544988166],"60":[1,0,0.33721654498816633],"66":[1,12,24.337216544988166],"69":[1,12,24.337216544988166],"72":[1,0,36.337216544988166],"_":96},{"48":[2,0,-28.99986030457525],"_":96},{"12":[3,0,-2],"24":[3,0,-14],"42":[3,0,-21],"60":[3,0,0],"_":96},{"0":[4,0,-7],"_":96},{"0":[4,0,-21],"_":96},{"0":[5,0,-3],"_":96}],[{"72":[0,16,2.0805446207650533],"_":96},{"0":[1,0,11.337216544988166],"12":[1,0,-8.662783455011834],"24":[1,0,18.337216544988166],"36":[1,0,53.337216544988166],"48":[1,0,-0.6627834550118337],"54":[1,0,12.337216544988166],"60":[1,0,0.33721654498816633],"66":[1,12,24.337216544988166],"69":[1,12,24.337216544988166],"72":[1,0,36.337216544988166],"_":96},{"48":[2,0,-28.99986030457525],"_":96},{"0":[3,0,2],"6":[3,0,2],"24":[3,0,5],"48":[3,0,-3],"84":[3,0,-5],"_":96},{"0":[4,0,-5],"_":96},{"0":[4,0,-19],"_":96},{"0":[5,0,-2],"_":96}],[{"72":[0,16,2.0805446207650533],"_":96},{"0":[1,0,11.337216544988166],"12":[1,0,-8.662783455011834],"24":[1,0,18.337216544988166],"36":[1,0,53.337216544988166],"48":[1,0,-0.6627834550118337],"54":[1,0,12.337216544988166],"60":[1,0,0.33721654498816633],"66":[1,12,24.337216544988166],"69":[1,12,24.337216544988166],"72":[1,0,36.337216544988166],"_":96},{"48":[2,0,-28.99986030457525],"_":96},{"0":[3,0,3],"6":[3,0,3],"24":[3,0,5],"48":[3,0,0],"84":[3,0,-2],"_":96},{"0":[4,0,-9],"_":96},{"0":[4,0,-24],"_":96},{"0":[5,0,-5],"_":96}]],[0,1,0,2,0,1,0,2,0,1,0,2,0,1,0,2],720],"mix":{"targetPeak":0.5,"masterGainDb":3,"softClipDrive":1.4}};
export default songData;
Exported JSON from this app is for the ZzFXTrack Player (ZzFXMicro 21-parameter format). It is not compatible with the modified ZzFXM player. To play exported songs in your game or other project you need the standalone player.
Alpha: There is no published npm package yet for the player. Use the player by copying the standalone package from this repo:
- Copy the
packages/zzfxtrack-player/folder into your project (the whole folder:index.js,src/,package.json,README.md). - Import in your code, e.g.
import { playZzFXTrackSong, stopZzFXTrackSong, buildSong } from './zzfxtrack-player/index.js';
(adjust the path to where you placed the folder). - Pass your exported song data (either the
[instruments, patterns, sequence, BPM]array or the{ song, mix }object from JSON) and anAudioContexttoplayZzFXTrackSong(songData, audioCtx, onEnded?, options?).
The package has no dependencies. See packages/zzfxtrack-player/README.md in this repo for the full API, song format, and a minimal example. A published package or separate repo may follow later.
System resources (patterns, blocks, arrangements, and instruments in the "System" folder) are part of the app’s codebase. You can edit and delete (in dev mode) them like any other resource, but they are treated as built-in presets rather than user data.
- Persistence: System resources live in the repository (
patterns/,arrangements/,blocks/,instruments.system.js, etc.). When you update or reinstall the app, these files may change. - Overwrites on update: Your changes may be overwritten when you update the app—system files are tracked in the repo and can change with new versions.
- Recommended workflow: If you want to keep customizations long-term, duplicate or copy system content into user resources (e.g. your own patterns/blocks/instruments), or export your work to JSON/WAV. Treat the "System" content as a starting point, not a storage location for personal work.
src/: main web app (Strudel REPL UI, Arranger, Blocks/tracker, export logic, and ZzFXTrack Player preview/offline render).patterns/,arrangements/,blocks/: pattern/arrangement/block definitions used by the app (including "System" content).instruments.js: user ZzFX instrument parameter definitions.instruments.system.js: built-in/system instruments shipped with the app.export.js: CLI exporter that writes JSON song data tooutput/.output/: generated export output (JSON and related artifacts).packages/zzfxtrack-player/: standalone ZzFXTrack Player package (no dependencies). For games/projects that play exported JSON. After changing player code inpackages/zzfxtrack-player/src/, run any sync/build scripts described inRELEASING.md(if needed).public/,index.html,vite.config.js: Vite-based frontend build and static assets.
ZzFXTrack is currently in pre-1.0 alpha. Breaking changes may occur between releases while the tooling and export pipeline are still being refined.
- Export format and player API: The JSON/JS export structure for the ZzFXTrack Player and the core player API (
playZzFXTrackSong,stopZzFXTrackSong,buildSong) are intended to be kept reasonably stable once published in a tagged release, but may still change before a 1.0 version. - UI and workflow: The UI layout, Strudel integration details, and in-app workflows may change more freely between versions.
- See
CHANGELOG.mdfor a summary of notable changes between releases.
The app is designed to work without user accounts.
- Local data: Projects, patterns, arrangements, blocks, and instruments you create in the UI are stored locally in the browser (e.g. local storage) and/or in files on your machine when running locally. Hosted builds do not upload your content to a backend server.
- Exports: When you export JSON or WAV, those files are written to your local filesystem (for local runs) or offered as downloads in the browser. You are responsible for managing and backing up these exports.
- Analytics/telemetry: There is currently no external telemetry or analytics pipeline described here; if this changes in the future, it will be documented in this README.
There is currently no formal browser/platform matrix yet. The focus is on modern desktop browsers with Web Audio support.
- Expect best results on up-to-date Chromium-based browsers (e.g. Chrome, Edge) on desktop.
- Other modern browsers (e.g. Firefox, Safari) may work but have not been systematically tested yet.
- Mobile browsers and touch workflows are experimental and may have audio/autoplay limitations.
ZzFXTrack is currently developed primarily for the author’s own projects, and a public repository/issues board may not yet be available.
- If/when a public repository and issue tracker are published, contribution guidelines will be added here.
- Until then, treat the app as an alpha tool: you are welcome to experiment with it and adapt the code for your own projects, but external contributions are not yet part of a formal process.
This repository is licensed under AGPL-3.0-or-later (see LICENSE).
This project also depends on and/or includes adapted code from:
- Strudel (
@strudel/*) -AGPL-3.0-or-later - ZzFX / ZzFXMicro -
MIT - ZzFXM for early boilerplate and inspiration
See THIRD_PARTY_NOTICES.md for attribution details and upstream links.
My background is in 90s demoscene and especially late 90s tracker scene. To this day, I'm interested in anything related to digital music production.

