The DUET engine, in your browser. DUET is an Impulse Tracker–compatible tracker whose SID channels are real 6581/8580 chips (reSID), and this page is that same engine — libduet, compiled to WebAssembly — with a player built around it. Whatever the tracker plays, this plays: Impulse Tracker modules, SID-Wizard modules, and DUET's own projects.
There is no server here. The page loads one WebAssembly module and reads your files inside the browser — a song you drop never leaves your computer, and closing the tab is all it takes to be rid of it.
The module lives in an AudioWorklet: it renders every 128-frame block the sound card asks for, so what you hear is the same bytes duet --render would write to a file. The note rows you see are stamped in the audio clock too — a row appears the moment it is heard, not the moment it was computed.
The header says which build is running and how big it is (libduet 0.1.0-alpha · ReleaseSmall 2.3 MB), then the song's own title and author. On the right, what kind of song it is: [IT] an Impulse Tracker module, [SWM] a SID-Wizard module, [DUET] a project, [CO] a co-play — an .it and a SID-Wizard module running on one clock.
Drop a song anywhere on this page and it plays. One file, several files, or a whole folder — the page walks a dropped folder itself, keeps every song it finds, and opens the first one.
A DUET project (.zon) is not one file: it names the song file it plays. Drop the project together with its .it / .swm, or simply drop the folder they both live in, and they arrive together.
The kind of file, its name, what it calls itself (read out of the song's own header), who made it, and — for a project — whether the song files it names were found. If they were not, the row says which is missing in red, and so does the status line when you try to open it.
Ninety songs ship with the page: DUET's own demos, every SID-Wizard example module, and Impulse Tracker modules by other musicians. Every one of them carries its author, its licence and where it came from — see ABOUT, or songs/MANIFEST.md.
?song=swm/congabeat.swm on the page's address opens that gallery song at once — any file under songs/ works, and a project fetches its song files beside it.
One column per channel or voice that sounds — columns appear as the song uses them. Each row is a row of the song, newest at the bottom, fading as it scrolls away, and each cell is the tracker's own spelling: the note, the instrument, and the effect.
The colours say which dialect a column speaks: blue an Impulse Tracker channel, amber a SID one, green a DUST synth slot, violet a DUSK slot. The Impulse Tracker columns share one clock; a SID-Wizard track has its own row count and scrolls at its own pace beside them, which is what a co-play looks like from inside.
The other view (V, or the VIEW button): the song's pattern as a tracker draws it, the playing row on the bar near the middle, the rows below it dim because they have not sounded yet. Row numbers are hex, and each cell is spelled exactly as DUET spells it — the same characters the tracker puts on screen, because the same library writes them.
An Impulse Tracker column shows note, instrument, volume and effect; a SID column shows the SID-Wizard face instead — no volume column, and a two-byte effect. A SID-Wizard track stands where its transport stands, on its own pattern and its own row. Columns appear as the song uses them, exactly as in the strip, and when there are more than fit the view follows the ones that are sounding. Clicking a header mutes here too.
Five ways to watch the master mix, all drawn the way DUET draws them in a terminal — a glow buffer under everything, scanlines over it.
A bigger window shows the same rows and the same effects, larger — the whole page is one design scaled to fit, never more rows in a smaller font. ?scale=1 pins it if you want the raw size.
The tracker format DUET is built on and the only module format it reads: samples, instruments, envelopes, NNAs, the resonant filter, the lot. Everything is played by DUET's own engine, judged against openmpt123 song by song.
Hermit's C64 tracker, ported here from its 6502 driver — the four extensions are one, two, three and four SID chips. The chips are real reSID emulations, and the register stream DUET produces is compared against the original driver's, row by row.
A project is a plain-text file that carries everything a song needs around its notes: which song file to play, the chips and their model, the DUST and DUSK synth patches, the mixer with its sends, the chord table, the markers. The notes stay in a real .it (or a SID-Wizard module) beside it — DUET has no private format, so every file it saves can be opened by other trackers too.
That is why a project needs its song file with it, and why the file list tells you when one is missing.
DUST is DUET's own SID synth — a chip per slot, its own tables, four slots. DUSK is a wavetable synth over the .it sample bank, eight slots, with a filter of its own. Both live in a project, and both show up in the note strip as their own columns.
You opened index.html straight from the disk. A browser gives such a page no origin and refuses its scripts, the audio worklet and the module. Serve the directory instead and open it over http: zig build serve, or python3 -m http.server in the folder, then http://localhost:8000/.
Click once anywhere on the page. A browser will not start audio until you have interacted with it — dropping a file or pressing a key counts too.
In red, on the status line: the build being served is the slow one and it cannot keep up with a heavy song. Whoever serves this page should run zig build wasm and reload. The header line always says which build you have.
Watch render in the number line. Near 100% the engine is using the whole audio budget: turn resample down to interpolate or fast, or try a lighter song. Two chips at resample is the heaviest thing here.
A project needs the song file it names. Open the whole folder, or drop the project and its song file together.
The status line says when a song ends — press Enter to start it again, or turn LOOP ∞ on. If the strip stays empty while the time runs, the song is playing something the strip has no column for yet; the visualiser will still show it.
Only Chrome and Edge can hand a page a folder it may open again. Elsewhere the folder is a one-visit snapshot — pick it again, or drop it.
An Impulse Tracker–compatible tracker with real SID channels, written in Zig: a terminal build, a window build, and this — the same engine three ways. The tracker edits; this page only plays.
The module and this page are GPL-2.0-or-later: DUET carries reSID by Dag Lem, which is GPL, so anything that links the engine is too. The font is JetBrains Mono under the SIL Open Font License. The full texts are in LICENSES/.
The SID-Wizard example modules are Hermit's (Mihaly Horvath) and his friends', under the WTF licence — DUET's whole SID-Wizard side is a port of his 6502 driver, and each module is credited by the name inside it. The Impulse Tracker modules were taken only where their author attached a licence that allows passing them on — Public Domain, CC0 or CC BY — and each one names its author, its licence and the page it came from in songs/MANIFEST.md. DUET's own demos are m64's and Claude's.
If you wrote one of these and would rather it were not here, say so and it goes — no argument, no delay.
Three files are the engine: duet-web.js (one class, DuetEngine), duet-worklet.js and duet.wasm. Put them beside your page, import the class, and a demo or a game plays any of these formats and syncs to the rows, the notes and the markers — embed.html is the whole of it in thirty lines. This player is that class with a page around it.
Everything here is in the DUET repository at github.com/M64GitHub/duetracker; the build stamp in the footer names the exact files. To host the player yourself, unzip duet-web-<version>.zip (or copy this directory) onto any static web server, under any path, and open it over https:// or from localhost — a page with an audio worklet needs a secure context, and nothing else: no special headers, no server-side code. The README in the directory says the same.
A keyboard to play the instruments, a shared .zip you can drop, and a few things m64 has not thought of yet.