BeatBax is a creative tool for making chiptune music in the style of classic game consoles. Instead of clicking through a tracker or a DAW, you write simple text scripts that describe your melodies, basslines, and beats - and BeatBax brings them to life with authentic retro sound. Whether you're a developer who loves the aesthetics of 8-bit audio or a musician who wants to explore the Game Boy sound chip, BeatBax gives you a fast, expressive way to compose, preview, and export your tracks. Not sure where to start? The built-in AI assistant can help you write and refine your songs just by describing what you want to hear.
Initial target hardware is the Nintendo Game Boy (DMG-01) APU and Nintendo Entertainment System (NES) Ricoh 2A03 APU. However, the architecture is designed so additional chip backends (PC-Engine, SID, Genesis) can be added later (see ROADMAP for further details).
BeatBax songs can be exported into multiple formats (JSON, MIDI, WAV) as well as chipset specific formats like hUGETracker (UGE) format for the Nintendo Gameboy.
An example screenshot of the BeatBax Web-UI.
- Simple text-based language — write melodies, basslines, and beats in a readable
.baxscript; no GUI required - 11 built-in effects — vibrato, arpeggio, portamento, pitch bend, sweep, volume slide, tremolo, pan, echo, note cut, and retrigger
- 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
- 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. - Web UI IDE — live editor with syntax highlighting, real-time validation, channel mixer, and one-click playback
- BeatBax Copilot — AI assistant that writes and edits songs from natural-language descriptions (bring your own API key)
- CLI tools —
play,verify,export, andinspectfor scripted and headless workflows - Extensible architecture — additional chip backends (C64 SID, Genesis YM2612) can be added as plugins without changing your songs
Note: BeatBax Copilot requires your own API key from any OpenAI-compatible provider (including local LLM) — no key is included or stored by BeatBax.
The quickest way to get started with BeatBax is to try out the Web UI which is available at: app.beatbax.com.
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:beatbax/instruments-gb/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 the BeatBax language format.
| 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 seffect 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
# 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) |
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.
Start the development server:
npm run web-ui:dev
# → http://localhost:5173Features:
- Monaco editor with
.baxsyntax highlighting (15+ token types, dark/light themes) - Live validation — red squiggles for undefined instruments, patterns, and sequences
- Resizable split-pane layout (state persisted to
localStorage) - Transport bar: Play, Pause, Stop, and ⚡ Live mode (800 ms debounce, auto-replays on edit)
- Menu bar with File, View, Playback, Export, and Help menus; full keyboard shortcut registry
- Unified channel mixer with per-channel mute, solo, and volume controls
- CodeLens inline actions —
▶ Previewand↺ Loopabove everypat,seq, andeffect; five note buttons (C3–C7) above everyinstfor instant timbre checks - Play selected (
Ctrl+Shift+Space) — play one or more selectedpat/seqlines simultaneously, each on its own channel - Command palette (
F1orCtrl+Alt+P) — export, validate, generate snippets, format, mute/solo by name - BeatBax Copilot — AI chat panel backed by any OpenAI-compatible endpoint (OpenAI, Groq, Ollama, LM Studio). Injects editor content and active diagnostics as context. Edit mode auto-applies generated code with up to 4 self-correction retries; Ask mode answers without touching the editor
- Settings panel — unified modal (
Ctrl+,orView → Settings…) with sections for General, Editor, Playback, Features, AI Copilot, and Advanced; most changes apply live without a page reload (exceptions: Auto-save and Audio backend / Sample rate take effect after reload)
Import statements are validated to prevent path traversal and unexpected file system access. See docs/language/import-security.md for the full policy.
import "local:lib/common.ins" # ✅ local-prefixed relative path
import "github:user/repo/branch/file.ins" # ✅ remote GitHub
import "https://example.com/drums.ins" # ✅ remote HTTPS
import "../../../etc/passwd" # ❌ path traversal — rejected
import "/etc/passwd" # ❌ absolute path — rejected
Important: Never execute untrusted .bax files without reviewing their import statements.
beatbax/
├── packages/
│ ├── engine/ # @beatbax/engine — core library (ESM, browser + Node)
│ │ └── src/
│ │ ├── audio/ # WebAudio playback + offline PCM renderer
│ │ ├── chips/ # Chip backends (gameboy/: pulse, wave, noise, APU)
│ │ ├── effects/ # Effect processors (vib, port, arp, sweep, …)
│ │ ├── export/ # JSON / MIDI / UGE / WAV exporters
│ │ ├── import/ # UGE reader (v1–v6), remote fetch cache
│ │ ├── instruments/ # Instrument state management
│ │ ├── parser/ # Peggy grammar, AST types, structured helpers
│ │ ├── patterns/ # Pattern expansion and transforms
│ │ ├── scheduler/ # Deterministic tick scheduler
│ │ ├── sequences/ # Sequence expansion
│ │ ├── song/ # Song resolver and ISM model
│ │ └── util/ # Logger, diagnostics, parse utilities
│ │
│ └── cli/ # @beatbax/cli — command-line interface
│ └── src/
│ ├── cli.ts # play, verify, export, inspect commands
│ └── nodeAudioPlayer.ts # Headless audio playback
│
├── apps/
│ └── web-ui/ # @beatbax/web-ui — browser IDE (Vite + TypeScript)
│ └── src/
│ ├── editor/ # Monaco integration, syntax highlighting, CodeLens
│ ├── panels/ # Channel mixer, Copilot, output panels
│ ├── playback/ # WebAudio playback bridge
│ └── export/ # Export dialogs and format handlers
│
├── bin/
│ └── beatbax # CLI entry point (Node shebang wrapper)
│
├── songs/ # Example .bax files
│ ├── *.bax # Full songs (sample, heroes_call, night_hawk, …)
│ ├── effects/ # One .bax demo per effect
│ ├── features/ # Feature-demonstration songs
│ └── gameboy/ # GB-specific examples
│
├── docs/ # Documentation
│ ├── language/ # Language reference (instruments, metadata, import security, …)
│ ├── exports/ # Export guides (UGE, WAV, MIDI)
│ ├── formats/ # Binary format specs (UGE v6, AST schema)
│ ├── api/ # API reference (scheduler, logger, UGE reader)
│ ├── chips/ # Sound chip hardware references (gameboy.md, …)
│ ├── ui/ # Web UI documentation
│ ├── contributing/ # Contributor guides (browser-safe imports, releasing)
│ └── features/ # Feature specs (active and complete/)
│
├── 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
npm install
npm run clean-all
npm run build-all
npm test| 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 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.
