QRPLAY
QRPLAY V3
A music box that fits in a QR code. Source on Codeberg.

scanning a code #
- scan with your phone's photo app or barcode app and copy all the text.
- in an empty browser tab, paste the text into the URL bar.
- press play!
making music #
notes and commands #
CDEFGAB
are musical notes. you can make a note sharp with/
or flat with:
. for example, C sharp isC/
and A flat isA:
.X
rests for the current note length.+-
changes the octave up or down. The default is the 4th octave, the one which contains middle C.WHQISTJ
changes the rhythm of the following notes. The note lengths are Whole, Half, Quarter, eIghth, Sixteenth, Thirtysecond, and sixtyfourth (J). the default is T.3
divides the current note length into triplets. for instanceQ3ABC
would play three notes that add up to one quarter note..
increases the current note length by 50%, soH.A
would play a note that lasts for three quarters.Vnn
sets the current volume to a number between 0 and 99. the default isV30
.V35
is twice as loud, andV40
is twice as loud as that.Unnn
sets the tempo in beats per minute. the default isU137
, for historic reasons.R
resets the pitch and note duration back to the defaults.
samples #
$𝑥...*
saves a sample in slot 𝑥. For instance, $0RSDDI+D-A*
would save the Megalovania riff into slot 0. you then play the sample like a note, with 0
.
the characters 012456789YZ
and space are QR-friendly and are reserved for storing samples. If you run out, you can also use [\]^_`{|}~
, newline, lowercase letters, or any UTF16 code unit.
note that 3
is missing as it is already used for triplets.
special case: if a sample ends with the letter V
, it will play asynchronously with the main track. for instance, you could play a chord like this: $0CV*0 G
.
This would play the C from the sample and the G from the main track at the same time.
samples keep separate octave and note length from the main track, but setting volume or tempo within a sample will change it globally when played.
samples can save and play samples, including themselves. if a sample plays itself it will loop. to prevent browser crashes, recursion and loops are capped at 100 iterations, and the limit decreases each time it is reached. if a sample saves into its own slot, you won't be able to play it again.
if a sample uses an incompatible character, then it cannot be played. you can use this to write comments, like $:TITLE: MY COOL SONG*
.
you can nest samples/other comments in your comment, but you can't use $
*
as ordinary text.
you can also write lowercase letters as comments if no samples are written for them, such as at the top, before your song.
making a QR code #
- make a copy of qrplay.html.
- remove all comments, leading whitespace, and trailing whitespace.
- remove all newlines.
- add
DATA:TEXT/HTML,
at the start, including the comma. - paste your song at the end.
- replace all spaces with
%20
,#
with%23
, newlines with%0A
, and%
with%25
if it is followed by two numbers. - use a QR generator like Zint to convert your code.
Your code will compress the best if it only uses the QR Alphanumeric subset. This means 0-9A-Z $%*+-./:
. There are no newlines or lowercase letters.
Zint allows you to mix data of different types, so you don't have to write your entire song this way, but it will save space if you do.
on a linux machine, with qrencode
installed, you can use make SONG=<filepath>
to do this automatically, or use make SONG=
to make a QR code with no song.
conversion #
QRPLAY is based on ZZM, which itself based on the ZZT Music Format. To convert from ZZT Ultra or ZZM to QRPLAY:
- uppercase all notes.
abcdefg
becomesABCDEFG
. - if converting from ZZM (not ZZT Ultra), add
R
to the start of every line. - replace sharps (
#
) with/
, flats (!
) with:
, and resets (@
) withR
. - collect
Z
channels together by their number (soZ01 A Z02 B Z01 C Z03 D
becomesZ01 AC Z02 B Z03 D
) - if the song uses any drums
012456789
, add these approximations at the beginning of the file. note that all of them will overwrite slot Z when played.
(at the default rate of 137bpm, J33 is 3.8ms, a little longer than the millisecond "clicks" used by ZZT. the approximations here are based on source code from The Reconstruction of ZZT.)$0$ZRJ33+++GV*ZX* $1$ZRJ33+B+C#DEFF#GV*ZX* $2$ZRJ33++G++DDB--G++DDBV*ZX* $4$ZRJ33+++EA-G--B++G++C#V*ZX* $5$ZRJ33++A#GA#G-A#+G-B+GV*ZX* $6$ZRJ33+A++C#-AAE+E--AV*ZX* $7$ZRJ33+FFED#DDC#V*ZX* $8$ZRJ33++DDDDDD#EV*ZX* $9$ZRJ33BA#BB-A+A#-BV*ZX*
- replace
Z
channels with immediately played async samples, optionally excluding the last one. for instance,Z01 AC Z02 B Z03 D
becomes$ZACV*Z $ZBV*Z D
.
to choose a slot for immediately played async samples:
- if any drum (or Y or Z) isn't used, you can use that slot.
- if all drums are used, you can use a lowercase letter like
z
. - if all drums are used but you can't use a lowercase letter (alphanumeric QR code), you can remove all spaces from the file and use space (
$ ACV* $ BV* D
). - if all drums are used but you can't use a lowercase letter or space (alphanumeric QR code with formatting or escaped spaces), you can use 0 and move the 0 drum sample to the start of each channel sample.
There is no equivalent of some ZZT ultra commands. You should remove them to avoid confusion.
- P priority override. alternative: remove the notes that you don't want to play.
- O octave override. alternative: use
R
to reset to octave 4 and-+
to choose an octave. - K echo mode. alternative: play the same sample twice with a slight delay and a volume change.
- % pitch sliding. alternatives: none. you will have to play staccato instead.
thanks #
- SArpnt for their hard work and helpful feedback to make the code as small and QR-friendly as possible, even as I kept trying to cram in "one more feature".
- asie for the Reconstruction of ZZT, which served as the blueprint for this project.
- Chris Allen for the ZZT Ultra Hall of Music, for documenting and showcasing the ZZM format and several awesome songs.
- Nicole Express for this blog post that helped me understand the timings.
- Thonky for the QR Alphanumeric Mode reference.
- the great people at Mozilla for documenting how to generate sound in a web browser, particularly the page about the OscillatorNode.
- there's a good chance this would not exist if I hadn't been exposed to Jesse's Bookmarklets back in the 2000s. Thanks, Jesse!