BeatBax is a live coding language and creative toolchain for making chiptune music in the style of classic 8/16 bit computers and game consoles. Instead of using a Tracker or DAW to develop your songs, you write your songs using a simple, but powerful grammar that describe your instruments, melodies, basslines, and beats - and BeatBax brings them to life with authentic retro sounds.
BeatBax currently supports Nintendo Game Boy (DMG-01) APU and Nintendo Entertainment System (NES) Ricoh 2A03 APU as built-in sound chips. Additional sound chip backends can be added as plugins (see ROADMAP for further details of currently implemented and future sound chips).
One of the main aims in creating BeatBax was to aid in the creation of songs for Homebrew games. So where possible, BeatBax songs can be exported into Tracker formats that game libraries can consume. For example hUGETracker (UGE) format for the Game Boy, or FamiTracker (Txt) for the NES. Standard output formats are also supported including WAV, MIDI and in some cases VGM - with additional chip specific output formats on the (ROADMAP.
Creating chiptunes is rewarding but can be hard and time-consuming. The BeatBax Desktop app includes BeatBax Copilot* — an AI assistant (BYOK) to help you write and edit songs. This is not a replacement for creativity, but it can help with construction, editing, and implementation of typical chiptunes practices to get the most out of the BeatBax grammar.
- Simple text-based grammar — create instruments, write melodies, basslines, and beats (in
.baxfiles) using a simple but powerful BeatBax grammar. - Authentic retro sound — Chip specific implementation, e.g.: 4-channel Game Boy DMG-01 emulation (pulse, wave, noise) and 5-channel NES Ricoh 2A03 emulation (pulse, triangle, noise, DMC) with hardware-accurate envelopes, duty cycles, and software macros
- Built-in effects — vibrato, arpeggio, portamento, pitch bend, sweep, volume slide, tremolo, pan, echo, note cut, and retrigger
- Reusable instrument libraries — share instruments across songs via
.insfiles; import locally or directly from GitHub - Export formats — MIDI, WAV, ISM JSON supported for all chips and one (or more) export format for each chip, e.g.hUGETracker v6 (
.uge) for GameBoy, FamiTracker Text for NES. - Desktop IDE — Electron + React client with native file dialogs, recent files, export, BeatBax Copilot, and the full IDE feature set
- Web-lite browser client — simplified try-in-browser editor/playback experience for quick demos and lightweight edits
- BeatBax Copilot* — AI assistant in the desktop app (BYOK)
- CLI tool —
play,verify,export, andinspectfor scripted and headless workflows, converting samples and more - Extensible architecture — additional chip backends (C64 SID, Genesis YM2612) can be added as plugins without changing the funadmentals of your song
*BeatBax Copilot (desktop only) requires your own API key from any OpenAI-compatible provider (including local LLM). Your API key is stored locally in the app only.
A .bax song defines instruments, effects, patterns, sequences, and a channel arrangement.
song name "An example song"
chip gameboy
bpm 128
# Import a shared instrument library (local or remote)
import "github:kadraman/beatbax-instruments/main/melodic.ins"
# Instruments
inst lead type=pulse1 duty=50 env={"level":12,"direction":"down","period":1,"format":"gb"}
inst bass type=pulse2 duty=25 env={"level":10,"direction":"down","period":1,"format":"gb"}
inst wave1 type=wave wave=[0,2,3,5,6,8,9,11,12,11,9,8,6,5,3,2,0,2,3,5,6,8,9,11,12,11,9,8,6,5,3,2]
inst snare type=noise env={"level":12,"direction":"down","period":1,"format":"gb"}
# Named effect presets
effect wobble = vib:8,4 # Vibrato: depth 8, rate 4
effect fadeIn = volSlide:+5 # Volume fade-in
effect arpMajor = arp:4,7 # Major chord arpeggio (root + major 3rd + 5th)
# Patterns
pat melody = C5<wobble> E4<fadeIn> G4<arpMajor> C5
pat bass_pat = C3 . G2<port:C4,50> .
pat drum_pat = snare . snare snare
# Sequences
seq lead_seq = melody:inst(lead) melody:inst(lead)
seq bass_seq = bass_pat:inst(bass)*2
seq wave_seq = melody:oct(-1):inst(wave1) melody:oct(-2):inst(wave1)
seq drums_seq = drum_pat*2
# Channel arrangement
channel 1 => inst lead seq lead_seq
channel 2 => inst bass seq bass_seq
channel 3 => inst wave1 seq wave_seq
channel 4 => inst snare seq drums_seq
play auto repeatPlease see the TUTORIAL for more details on how to use the BeatBax grammar.
TBD
BeatBax songs can make use of built-in instrument and
| Effect | Syntax | Description |
|---|---|---|
| Pan | pan:L|C|R or gb:pan:-1.0…1.0 |
Stereo panning |
| Vibrato | vib:<depth>,<rate>[,<wave>[,<dur>[,<delay>]]] |
Pitch LFO |
| Portamento | port:<speed> |
Smooth pitch glide from previous note |
| Pitch bend | bend:<semitones>[,<curve>[,<delay>[,<time>]]] |
Musical pitch bend |
| Sweep | sweep:<time>,<dir>,<shift> |
GB hardware NR10 frequency sweep |
| Arpeggio | arp:<offset1>,<offset2>[,…] |
Rapid note cycling to simulate chords |
| Volume slide | volSlide:<±amount> |
Per-tick volume automation |
| Tremolo | trem:<depth>,<rate>[,<wave>] |
Amplitude LFO |
| Note cut | cut:<ticks> |
Gate note after N ticks |
| Retrigger | retrig:<rate>[,<vol>] |
Rhythmic note restart (WebAudio only) |
| Echo | echo:<delay>,<feedback> |
Feedback delay (WebAudio only) |
Annotated examples for effect are in chip specific directories songs/features/**.
Export compatibility:
| Effect | JSON | MIDI | UGE | FamiTracker Text | WAV |
|---|---|---|---|---|---|
| pan, vib, port, arp, volSlide, cut | ✓ | ✓ | ✓ | ✓ | ✓ |
| bend | ✓ | ✓ | Approx. (3xx portamento) | 3xx portamento | ✓ |
| sweep | ✓ | ✓ | Instrument-level only | Instrument-level only | ✓ |
| trem | ✓ | ✓ | Metadata only | Metadata only | ✓ |
| retrig, echo | ✓ | ✓ | — | — | — |
Windows note: npm has limitations passing flag arguments through
npm run. Usenode bin/beatbaxor thebin\beatbaxwrapper directly.
# Validate a song file
node bin/beatbax verify songs/sample.bax
# Play (headless by default in Node.js)
node bin/beatbax play songs/sample.bax
node bin/beatbax play songs/sample.bax --browser # open Web UI instead
# Export
node bin/beatbax export json songs/sample.bax output.json
node bin/beatbax export midi songs/sample.bax output.mid
node bin/beatbax export uge songs/sample.bax output.uge
node bin/beatbax export wav songs/sample.bax output.wav
# Convert a WAV into a raw NES DMC sample
node bin/beatbax convert wav2dmc samples/wav/low_kick.wav --dmc-rate 15 --emit-inst
# Inspect a .bax or .uge file
node bin/beatbax inspect songs/sample.bax
node bin/beatbax inspect output.uge --json| Flag | Description |
|---|---|
--browser / -b |
Launch browser-based playback via Vite |
--headless |
Force Node.js headless playback (default) |
--backend <name> |
auto (default), node-webaudio, browser |
--sample-rate <hz> / -r |
PCM sample rate (default: 44100) |
--buffer-frames <n> |
Offline render buffer size |
| Flag | Applies to | Description |
|---|---|---|
--out <path> |
all | Output file path |
--duration <seconds> |
midi, wav | Override auto-calculated duration |
--channels <list> |
midi, wav | Export only listed channels (e.g. 1,3) |
convert wav2dmc turns a 16-bit mono/stereo PCM WAV into a raw NES .dmc sample for type=dmc instruments:
node bin/beatbax convert wav2dmc samples/wav/low_kick.wav --dmc-rate 15 --emit-inst --playThe output is a headerless DMC byte stream. Playback settings live on the BeatBax instrument, so the converter prints the matching line when you pass --emit-inst:
inst kick type=dmc dmc_rate=15 dmc_loop=false dmc_sample="local:samples/wav/kick.dmc"
Useful controls:
| Flag | Description |
|---|---|
--dmc-rate <0-15> / -q |
DMC rate used for encoding and playback preview. 15 is fastest/highest quality; lower values are darker and shorter-bandwidth. |
--dmc-loop |
Use dmc_loop=true in emitted snippets and loop the preview. |
--trim-silence <db> / --no-trim-silence |
Trim quiet WAV tails before encoding; this is often the most useful control for reducing DMC hiss. |
--tail-ms <ms> |
Keep a small amount of audio after the last above-threshold sample. |
--fade-out-ms <ms> |
Fade the end before encoding to avoid noisy/clicky tails. |
--max-duration-ms <ms> |
Hard cap the source duration before encoding. |
--ntsc / --pal |
Select the DMC hardware rate table (--ntsc is default). |
speakernpm module (best quality — install withnpm install --save-optional speaker)play-soundwrapper (cross-platform system players)- System command (
PowerShell/afplay/aplay)
WAV export uses a direct PCM renderer (packages/engine/src/audio/pcmRenderer.ts) with no WebAudio dependency. It implements all four Game Boy channels (duty, envelope, wavetable, LFSR noise) and outputs stereo 44100 Hz 16-bit PCM. See docs/exports/wav-export-guide.md.
BeatBax Desktop ships the full IDE: native menus and file I/O, export, Copilot, channel mixer, pattern grid, advanced Monaco editor, and cross-platform installers. See apps/desktop/README.md for more details.
Download installers from GitHub Releases (tags desktop-v*).
Installers are not code-signed yet. Windows SmartScreen and macOS Gatekeeper may warn on first install or launch. See README.txt in the install folder (next to the BeatBax application) for platform-specific steps.
Start the development server:
npm run desktop:devThe browser app is the web-lite profile: a lighter try-in-browser surface for editing, validation, and playback. Export, CoPilot, mixer, and other IDE features require the desktop app. See apps/web-ui/README.md more details.
Try the latest version of the web-ui at app.beatbax.com.
Start the development server:
npm run web-ui:devThen browse to: http://localhost:5173
beatbax/
├── apps/
│ ├── desktop/ # BeatBax Desktop (Electron + React, desktop-full)
│ └── web-ui/ # BeatBax web-lite browser client
│
├── packages/
│ ├── engine/ # Live-coding language and runtime
│ ├── app-core/ # Shared client logic (web-lite + desktop-full)
│ ├── cli/ # Command-line interface
│ └── plugins/
│ ├── chip-sms # Sega Master System / Game Gear SN76489
│ ├── chip-spectrum-128 # ZX Spectrum 128 / Amstrad CPC AY-3-8912
│ ├── export-famitracker # FamiTracker text exporter
│ ├── export-vgm # VGM exporter
│ └── ...
│
├── bin/
│ └── beatbax # CLI entry point (Node shebang wrapper)
│
├── songs/ # Example .bax files
├── docs/ # Documentation
├── schema/
│ └── ast.schema.json # JSON Schema for the BeatBax AST
│
├── tests/ # Root-level integration tests
├── scripts/ # Build and tooling scripts
├── examples/ # Standalone code examples
└── media/ # Logo and promotional assets
| Topic | Location |
|---|---|
| Tutorial | TUTORIAL.md |
| Roadmap | ROADMAP.md |
| Releasing (npm + desktop) | docs/releasing.md |
| Desktop app | apps/desktop/README.md |
| Web-lite client | apps/web-ui/README.md |
| Dev notes | DEVNOTES.md |
| Contributing guide | CONTRIBUTING.md |
To build the BeatBax engine and all plugins, carry out the following:
npm install
npm run clean-all
npm run build-all
npm testThen you can run the development Desktop UI or Web-UI as described above.
| Script | Description |
|---|---|
npm run engine:build |
Build @beatbax/engine |
npm run cli:build |
Build @beatbax/cli |
npm run web-ui:dev |
Start Web UI dev server |
npm run desktop:dev |
Start the desktop Electron + React app |
npm run desktop:build |
Build the desktop app bundles |
npm run desktop:test |
Run desktop unit tests |
npm run desktop:dist |
Create desktop installers |
npm run cli:dev |
Build engine + run CLI dev entry |
npm run build-all |
Full monorepo build |
npm run clean-all |
Clean all dist outputs |
npm test |
Run all test suites |
# Terminal 1
npm run web-ui:dev
# Terminal 2 — after changing packages/engine/src/
npm run engine:build
# Then press r+Enter in Terminal 1 to restart ViteIf the restart doesn't pick up changes:
cd apps/web-ui && npm run dev:clean # --force bypasses Vite cachenpm run engine:build
node scripts/link-local-engine.cjs # copies dist into node_modules
node bin/beatbax play songs/sample.bax --headlessnpm run build-all
npm link
beatbax --helpContributions welcome. Open issues for features and PRs against main. Keep changes small and include tests for parser/expansion behaviour. See CONTRIBUTING.md.
MIT — see LICENSE.

