MOVE: ↑↓←→·WASDA: Z·J B: X·K
Playload
A tiny game engine that lets you share games as URLs
Specifications
- Up to 3 KB of compressed JavaScript
- API with 6 functions, 2 variables and 1 callback
- 160 × 144 pixels, 8 colors, CRT display
- Built-in 8 × 8 Unicode letters, symbols and emoji
- ZX 128 PLAY-compatible, 3 sound channels
- 4-direction and 2-button input
- The URL’s payload is the whole game — Playable Payload... Playload!
Create and share
Write JavaScript code in the CODE editor and press Run to execute it. Your code is stored in the URL, so you can share it simply by sending the URL. To save your code, bookmark the URL.
Start your code with // @title My Title to set the title shown in
browser tabs and bookmarks.
Support Playload
If you enjoy Playload, please consider starring the project on GitHub. It helps more people discover the project.
Playload is free to use. Donations are also welcome through GitHub Sponsors, Patreon or Ko-fi to help cover development costs and give me more time to improve Playload.
License
Playload's code and documentation are licensed under MIT.
Bundled third-party materials retain their own licenses:
- Public Pixel: CC0
- Unscii-8: public domain / CC0
- Twemoji color reference: CC BY 4.0
- Misaki Gothic: use, modification and redistribution permitted, including commercial use
- Unicode data: Unicode License v3
- DB8 (palette): Richard Fhager (DawnBringer)
- CRT-Lottes: public domain
- AY volume measurements: public domain
- Prism: MIT
- Prettier: MIT
- Press Start 2P (logo and favicon): SIL OFL 1.1
- JetBrains Mono (UI and editor font): SIL OFL 1.1
- emu2149 (test reference): MIT
API
Functions
- cls(col)
Fill the character screen with col.
cls(0)- put(cx, cy, str, col1?, col2?)
Cell text stays; foreground/background, omit/null = PCG.
put(1, 1, "HI\n👾") put(1, 1, "HI\n👾", 6, 0) put(1, 1, "HI\n👾", null, 0)- spr(x, y, str, col)
8px-spaced text; newlines, transparent, this tick.
spr(76, 68, "👾", 6) spr(72, 64, "AB\nCD", 6)- btn(id)
True while the button is held.
if (btn(3)) put(1, 1, "→") // Held- btnp(id)
New press; same IDs as btn.
if (btnp(4)) play(1, "V12O6N1C")- play(ch, str)
Play sound on channel 0–2 using ZX 128 PLAY.
play(0, "T150V12O5N3ceg))") play(0, "") // Stop
Variables
- pcg[chr]
8-byte pattern and foreground/background colors.
pcg["↑"].pat[0] = 0x18 pcg["↑"].pat = "183c7e1818181800" pcg["👾"].col = [6, 0]- scr[cy][cx]
Cell characters and colors; null follows PCG.
scr[4][3].chr = "👾" // [cy][cx] scr[4][3].col = [null, 1]
Callback
- tick()
Update the game at 60 Hz.
function tick() { // 60 Hz spr(76, 68, "👾", 6) }
ZX 128 PLAY
Notes
- cdefgab
Notes in the current octave.
"ceg"- CDEFGAB
Notes one octave higher.
"CEG"- O0–O8
Set octave; O5c is middle C.
"O5c"- # · $ · &
Sharp, flat and rest.
"#c $b &"
Lengths
Write the length before notes: 5ceg = quarter notes. Default: 5.
1 1/162 3/323 1/84 3/165 1/46 3/87 1/28 3/49 110 1/16 triplet11 1/8 triplet12 1/4 triplet- 10–12
Triplets apply to the next three notes.
"10cde"- _
Tie by adding note lengths together.
"3_5c"
Tempo
- T60–T240
Tempo in BPM; set on channel 0.
"T150"
Volume & Envelope
- V0–V15
Set volume, from silent to loud.
"V12"- U · X0–X65535
U: envelope unless V is used. X: period (X0 = X1/2).
"UX1000W6" "X0"- W0–W7
Select a volume envelope shape.
W0decay → 0W1attack → 0W2decay; hold 0W3attack; hold 15W4repeat decayW5repeat attackW6attack / decayW7decay / attack
Mixer
- M1–M63
Tone bits 1/2/4; noise bits 8/16/32.
"M7" "M56"
Control
- (…) · ))
Repeat twice / loop forever.
"(ceg) ceg))"- H
Stop all channels at this point.
"cegH"- N
Separate adjacent numbers.
"O5N3ceg"- !…!
Ignore text between ! marks.
"!intro!ceg"