Skip to content

About

Browser-based Game Boy & Game Boy Color music tracker with cycle-accurate APU emulation, works on desktop and mobile. Export to ROM, GBS, VGM, GBDK C arrays, XM/MOD, WAV/FLAC/Opus, stems and video.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

GB Tracker

A tracker for Game Boy music that runs in the browser — on desktop with a keyboard and on phones with touch. Sound comes from a cycle-accurate emulation of the Game Boy APU (DMG and Game Boy Color), not from imitation oscillators. What the tracker plays are the same register writes a sound driver makes on real hardware.

Live: https://gb.chiptrackr.com/

Manual: docs/MANUAL.md, also on the site: https://gb.chiptrackr.com/manual.html

What comes next is in the roadmap.

Features

  • 4 channels as on the hardware: 2 × pulse (channel 1 with sweep), wave, noise
  • Pattern editor with order list, 15 instruments per channel type and 16 drawable waveforms (or imported from audio)
  • Samples (drums, voices) on the wave channel, streamed into wave RAM from the timer interrupt like LSDJ kits
  • Instrument macros: per-tick tables for volume, arpeggio, pitch and duty/waveform/noise width, with loop points
  • Effects: arpeggio, slides, portamento, vibrato, volume slide, delay, cut, panning, master volume, speed, jump/break
  • DMG or GBC mode per song
  • Oscilloscope per channel, click to mute
  • Song library in the browser (save, open, rename, duplicate, delete), JSON import/export, ZIP backup of the library, automatic working-copy backup
  • Exports: playable Game Boy ROM (.gb/.gbc), GBS, XM, MOD, VGM, WAV, FLAC, Opus, per-channel stems, a WebM video with scopes and the scrolling pattern, and a GBDK-2020 project (C array + tiny driver + Makefile)
  • Full-window tracker layout in Game Boy colours (off-white, A/B-button red) with colour-coded pattern columns, light and dark mode
  • Responsive: desktop with side lists, tablet two-column, phone with views and an on-screen keypad
  • On-screen piano (mouse, touch, glissando) and Web MIDI input from any connected keyboard
  • Master volume for playback
  • Installable app (PWA): works offline after the first visit, on phones and desktop
  • Interface in English, German, Spanish and French (picked from the browser language, switchable in the toolbar)

Exports

All hardware exports share one base: the song captured as the exact APU register writes the player makes per frame (src/export/stream.ts). Playing that stream back reproduces the tracker output sample for sample, which the tests check.

  • GBDK project (.zip): gbtr_player.c/.h (a ~40 line driver), the song as const uint8_t array, an example main.c and a Makefile. Call gbtr_play(song_data, song_loop) once and gbtr_update() every frame. The C code is plain C99 with direct register writes, so it also works with other SDCC-based setups.
  • Game Boy ROM (.gb / .gbc): a cartridge image that plays the song in a loop and shows title, author, model and a play timer. It boots on real hardware, flash carts and emulators. Songs longer than one 16 KiB bank use MBC5 banking. CGB songs get the CGB flag and a white/red palette.
  • XM / MOD: FastTracker 2 and ProTracker modules for other trackers. Instruments become samples (one period of the pulse or wave, or the LFSR noise sequence) tuned to the exact pitch; XM gets volume envelopes, MOD gets them rendered into the samples. Effects are translated; what cannot be expressed is listed after the export. CI cross-checks both formats with libopenmpt (patterns, length and the pitch of rendered notes).
  • GBS: Game Boy Sound System v1 for music players (VBlank rate). Same driver as the ROM.
  • VGM: VGM 1.71 with the Game Boy DMG chip, including loop point and GD3 tags.
  • WAV: 16-bit stereo, rendered by the emulator.
  • FLAC: the same audio, lossless (own encoder, checked bit for bit against ffmpeg).
  • Stems: one WAV per channel in a ZIP.
  • Opus / video: Ogg Opus audio and a 1280×720 WebM video (scopes and pattern), encoded with WebCodecs and muxed by our own Ogg and WebM writers (checked against ffmpeg).

Stream format used by the C driver:

Byte Meaning
0x00–0x2F, vv write vv to register 0xFF10 + byte
0x81–0xFE wait byte & 0x7F frames
0xFF continue at the loop offset

The ROM and GBS contain a hand-assembled SM83 version of the same driver at 0x0400; the stream starts at 0x4000 (bank 1) and the driver switches banks by writing to 0x2000. The tests run both images on a small SM83 interpreter (tests/export/sm83.ts) and compare every register write with the reference driver, across bank boundaries and the loop point.

Development

npm install
npm run dev     # development server
npm test        # hardware and player tests
npm run build   # production build into dist/
npm run smoke   # browser smoke test of dist/ (add --firefox or --webkit)

npm run smoke serves the built site and drives it with Playwright. At desktop, tablet and phone sizes (in WebKit: emulated iPhones and an iPad) it checks that the tracker loads without errors or sideways scrolling, that playback starts and stops, that the phone view switcher, the on-screen piano and the keypad work and leave the pattern room on small and sideways phones, that typed notes are saved and survive a reload, and that every export hands over a file with the right header. It also checks that German, Spanish and French browsers get their UI language without breaking the layout, and that the manual loads with its contents and images. An optional folder argument collects screenshots. CI runs it in Chromium, Firefox and WebKit.

Every push to main is tested, built and published to GitHub Pages by GitHub Actions.

Translations

UI strings live in src/i18n/locales/. English (en.ts) is the base locale and defines every key; other locales may lag behind and fall back to English. To add a string, add a key to en.ts and use t('key') in code (or data-i18n, data-i18n-title, data-i18n-aria-label in static markup). To add a language, create locales/<code>.ts and register it in LOCALES in src/i18n/index.ts.

Manual

The manual is written in docs/MANUAL.md; the build renders it into manual.html on the site (src/manual/). tests/docs/manual.test.ts fails when an effect, export button or tab is missing from the manual, so update it in the same PR as the feature.

Analytics

The repository contains no analytics. A deployment can add its own snippet at build time: the Pages workflow passes the repository variable TRACKING_CODE (raw HTML, e.g. the <script> tags of your analytics service; Settings > Secrets and variables > Actions > Variables) to the build, which puts it unchanged at the end of <head> of every page (src/analytics/plugin.ts). Without it, as in local, dev and CI builds and forks, nothing is added. Whether Do Not Track is respected is up to the snippet; many services have an option for it.

The tracker reports user actions as gb-tracker:track window events with { name, data } as their detail (src/analytics/track.ts): play (mode), stop, export (format), import, song-new, song-demo, song-save, song-save-as, library (action), theme, language, piano, midi, about, about-game (score), pwa-install and pwa-launch. To forward them, add a listener to the snippet, for example:

<script>
  addEventListener('gb-tracker:track', (e) => window.myAnalytics?.track(e.detail.name, e.detail.data));
</script>

The service worker only handles requests inside the site's own scope, so external analytics scripts are never cached for offline use.

Hardware coverage

The APU tests in tests/apu/ check the documented behaviour of the original hardware (Pan Docs, gbdev wiki "Gameboy sound hardware") and are organised after Blargg's dmg_sound test ROMs:

Blargg test Covered in
01-registers registers.test.ts (read masks, writes while powered off, wave RAM)
02-len ctr length.test.ts
03-trigger length.test.ts (extra length clock, trigger loads 63/255)
04-sweep sweep.test.ts
05-sweep details sweep.test.ts (negate quirk)
06-overflow on trigger sweep.test.ts
07-len sweep period sync length.test.ts (frame sequencer: length 0/2/4/6, sweep 2/6, envelope 7)
08-len ctr during power length.test.ts (DMG keeps lengths, CGB clears them)
09-wave read while on channels.test.ts
10-wave trigger while on channels.test.ts (DMG wave RAM corruption)
11-regs after power registers.test.ts
12-wave write while on channels.test.ts

On top of that: duty patterns, envelope including "zombie mode", DACs, noise LFSR (15/7 bit, period length), mixer (NR50/NR51), high-pass filter, PCM12/PCM34 (CGB only) and end-to-end tests that measure the pitch of the emulated output.

Limits of the emulation: it works at CPU clock resolution (4.19 MHz). Effects below one clock, such as the exact position in the wave channel's 2 MHz grid, or the revision-specific differences of zombie mode between individual DMG chips, are approximated.

About

Browser-based Game Boy & Game Boy Color music tracker with cycle-accurate APU emulation, works on desktop and mobile. Export to ROM, GBS, VGM, GBDK C arrays, XM/MOD, WAV/FLAC/Opus, stems and video.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages