What we've reverse-engineered about the per-sprite drawing replay — the recording of how a
sprite was drawn, used by the archive's replay player (/load/, see static-site/generate.mjs +
postmaster.js, which call loadReplayFromURL) and as a moderation signal
(static-site/moderation/scan-replay.mjs). Documented 2026-06-02 from live data.
- URL:
https://<id%4>.pixiecdn.com/sprites/<id>/replay.json(the same0..3.pixiecdn.comshard scheme as the images). CORS: GET only (a cross-origin HEAD gets noAccess-Control-Allow-Origin). - Only exists for ids ≳ 110,000 (the post-2017 editor that records replays). Older sprites return 404 (no replay was ever captured). Per the archive notes, re-uploads load but don't animate — so a 404 on a post-2017 id can indicate a re-upload rather than a drawn sprite.
A plain JSON array of strokes; each stroke is an array of pixel edits:
[
[ {"x":0,"y":5,"color":"rgba(0, 0, 0, 1)"}, {"x":1,"y":5,"color":"rgba(0, 0, 0, 1)"}, … ],
[ … next stroke … ]
]coloris a CSSrgba(r, g, b, a)string.- A stroke ≈ one mouse-down→up. ops =
array.length(number of strokes); total pixels = sum of stroke lengths.
A JSON object:
{
"version": "1",
"palette": ["#000000", … 32 colors …],
"history": [ { "name": "PutImageData", "x": 17, "y": 29, "imageData": {"data":"II4…","width":…,"height":…} }, … ],
"initialState": { "data": "II18…", … }
}version:"1".palette: the editor's color palette (observed length 32).history: ordered array of edit operations, each{ name, … }. Observednamevalues:PutImageData{x, y, imageData:{data,width,height}}— paints a region (a brush stamp, a fill, or a paste).Resize{size:{width,height}, sizePrevious:{…}, imageData:{…}}— changes canvas size (an upload typically resizes the canvas to the image's dimensions).- (Other op names may exist; treat unknown names as ordinary edits.)
initialState: the starting canvas.- ops =
history.length.
imageData.data and initialState.data are a proprietary string codec (the "II18…" /
"II4…" blobs), not standard base64 — decoding them needs the editor's codec. For pixel-level
analysis, decode the sprite's original.png instead; replay.json is best used for op structure,
not pixels.
| Signal | Detection | Meaning |
|---|---|---|
empty |
v0 ops = 0 (0 px) | Blank stroke array = a genuinely blank canvas — likely trash (sev 2) |
no-edits |
v1 history = 0 |
NOT necessarily blank — initialState may hold an uploaded image that was never edited (some are ~22 KB PNGs). Treat as an upload SIGNAL, review on merit (sev 1) |
single-stroke |
v0 ops = 1 (px ≈ whole canvas → a paste) | One action |
single-op |
v1 history.length = 1 |
One action |
single-resize-upload |
v1 single op is Resize |
Canvas resized once = an uploaded image |
large-canvas |
v1 Resize size max-dim > 256 |
Big canvas (e.g. 1280×720) — upload (real pixel art rarely this large) |
no-replay |
404 on id ≳ 110k | Possibly a re-upload |
It also recovers dimensions for post-2017 sprites (from Resize.size) — useful because the
dataset only carries w/h for the 2017-DB rows.
An upload/paste is a SIGNAL, not a verdict. It does not mean the content is poor — people paste perfectly good art, and off-topic raster dumps are common but not always removable. Use these flags to prioritize human review (the review tool: remove / adult / false-positive), never as an automatic takedown. Only
emptyis a strong stand-alone "trash" signal.
- The full set of v1
historyop names (onlyPutImageData+Resizeobserved). - The
imageData/initialStatestring codec (would let us reconstruct frames without the editor). - Exact id where v0→v1 switched (somewhere between ~140k and ~144k in samples).