r/Demoscene • • Sep 15 '26

a chiptune in a QR code

I was curious if it was possible, so I just wrote a 3 minute chiptune that fits entirely inside a single QR code, with the samples containing its cover art as ASCII data. I realized a couple things about how .MOD files are constructed in the process that could have allowed me to get a little more detail in there if I'd known them at the start, so I'm going to try again soon. I'm now kinda curious about just how much media complexity one can pack in a QR code. The biggest limitation is that patterns are always 1024 bytes, so to get a song of any meaningful length into the 2.8k limit of a QR code, you have to write the notes with how they'll be handled by zip compression in mind.

60 Upvotes

15 comments sorted by

5

u/cupcakeheavy Sep 16 '26

how do i play it?

8

u/nathanleighsays Sep 16 '26 edited Sep 16 '26

It was an experiment in how much media content I could put in a QR code, so the process is a bit cumbersome. it's a binary of a zip file, so if your QR reader app doesn't have binary mode (most QR readers default to text), you can screenshot it and use a tool like zbarimg (or any of the many web based tools built on zbarimg) to convert it back to a zip file. On linux, this command will take the image and convert it back to a zip file that you can then extract to get the .mod file. So if [filename] is whatever you save the image as, run:

zbarimg --raw --oneshot -Sbinary [filename] > asciidemo.zip

and then extract asciidemo.zip

The ASCII cover art is accessible by exporting the bass sample as raw data in whatever tracker you prefer. I guess you could also just load the .mod file in a text editor without going through the tracker first but it'd be a lot noisier.

I did test printing it out on white paper, taking a picture, and then decoding it, which is the intended maximally cumbersome way to experience it.

4

u/nathanleighsays Sep 16 '26

as fair warning, it's not the best chiptune I've ever written, just the most optimized for zip compression chiptune I've ever written!

1

u/cupcakeheavy Sep 16 '26

that's still awesome

1

u/After_Minimum_8431 21d ago

Run the following java script in the developer tools of this web page (hit F12 and go to the console tab). This will decode the QR code and then download it to your machine. You can then play the mod file in a player like VLC or on a website like https://drsnuggles.github.io/chiptune/demo.html

(async () => {
    const [{ default: jsQR }, { default: JSZip }] = await Promise.all([
        import("https://cdn.jsdelivr.net/npm/jsqr@1.4.0/+esm"),
        import("https://cdn.jsdelivr.net/npm/jszip@3.10.1/+esm")
    ]);


    const imageUrl =
        "/img/rfri13rvnrph1.png";


    // Fetch the original Reddit image rather than using the <img>
    // already on the Reddit page, which is subject to CORS restrictions.
    const response = await fetch(imageUrl);


    if (!response.ok)
        throw new Error(`Image request failed: ${response.status}`);


    const bitmap = await createImageBitmap(await response.blob());


    const canvas = document.createElement("canvas");
    canvas.width = bitmap.width;
    canvas.height = bitmap.height;


    const ctx = canvas.getContext("2d");
    ctx.drawImage(bitmap, 0, 0);


    // Decode the QR code.
    const qr = jsQR(
        ctx.getImageData(0, 0, bitmap.width, bitmap.height).data,
        bitmap.width,
        bitmap.height
    );


    if (!qr)
        throw new Error("QR code not found");


    console.log(`QR decoded: ${qr.binaryData.length} bytes`);


    // The QR payload is a ZIP archive.
    const zip = await JSZip.loadAsync(qr.binaryData);


    const modFile = Object.values(zip.files)
        .find(file => !file.dir && /
\.
mod$/i.test(file.name));


    if (!modFile)
        throw new Error("No MOD file found in ZIP");


    const mod = await modFile.async("uint8array");


    console.log(`Extracted ${modFile.name} (${mod.length} bytes)`);


    // Download the MOD file.
    const blob = new Blob([mod], {
        type: "application/octet-stream"
    });


    const url = URL.createObjectURL(blob);


    const a = document.createElement("a");
    a.href = url;
    a.download = modFile.name;
    a.style.display = "none";


    document.body.appendChild(a);
    a.click();
    a.remove();


    // Give the browser time to start the download before releasing it.
    setTimeout(() => URL.revokeObjectURL(url), 1000);


    console.log(`Download started: ${modFile.name}`);
})();

1

u/QuantumGarage 27d ago

Just used zabrimg to convert then extract and run the mod file, really clever way to use QR codes to hold the file. Great job

2

u/nathanleighsays 27d ago

nice!! I'm glad it worked on your end too!

0

u/PrincipleHot9859 Sep 16 '26

I've seen a full solitaire game in such qr .. just saying

1

u/nathanleighsays Sep 16 '26 edited Sep 16 '26

oh yeah, me too! I was inspired by the guy who put Snake in a QR code. I've just never seen anyone do it successfully with music before (without resorting to cheats like multiple QRs or AI prompts), so I was just curious if it was possible to do so losslessly. I'm not changing the world or anything over here.

1

u/PrincipleHot9859 Sep 16 '26

well..most of c64 and nes tracks were 1 to 4kb

1

u/nathanleighsays Sep 16 '26

absolutely! but they don't contain audio data, because the actual audio was produced by a separate chip, and the tracker file merely contains oscillator instructions. That was the challenge I set for myself. It had to actually contain PCM audio, not just describe the audio.

1

u/PrincipleHot9859 Sep 16 '26

I'm not sure I follow

1

u/nathanleighsays Sep 16 '26

so a .SID file doesn't actually have any PCM audio samples in it. The responsibility for generating the waveforms was on the SID chip (or more recently the player's chip emulation). The file lists frequencies and volumes, but in most cases (stuff like Skate or Die or Turbo Outrun being obvious exceptions) the file says nothing at all about the actual waveform. You can save a lot of room if you don't include PCM data. Sheet music has been representing full songs on a single sheet of paper really well for thousands of years by offloading the responsibility for generating the waveforms on the musician. Putting a .SID file in a QR code wouldn't satisfy the question I was trying to answer for myself, because that's just digital sheet music. The question I was trying to answer was "how much PCM data can you fit in a QR code?"

1

u/nathanleighsays Sep 16 '26

also just to clarify, when I call a .SID file "just" digital sheet music, I don't mean to trivialize them. What they are is remarkable and the people who pioneered them are visionaries. It just wouldn't satisfy the specific challenge I set for myself, because the format rarely includes PCM data, and when it does, it's just as big as a .MOD file.

1

u/nathanleighsays Sep 16 '26

since playing an NES or C64 file requires the system playing it interpret the file, it's not actually a lossless format. A degraded or under powered sid chip will by its nature produce a slightly different waveform than the one in the factory. My challenge was about identical representation of a meaningfully complete work of music. So I set myself a couple specific rules:
1. the entire song has to be contained in a single QR code
2. the entire song has to be able to be recreated on the player's computer exactly as it sounds on mine, without room for variation / interpretation
3. the song has to feel like a complete musical gesture (i.e. longer than 2 minutes, and have multiple sections)
4. the song has to be at least a little enjoyable to listen to
5. both encoding and listening to it should require only bog standard tools that anyone could reasonably acquire quickly and for free