THE IDEA
Naïve art is deliberately unskilled-looking figurative drawing — the wobbly line, the mismatched eyes, the hatching that spills past the edge. It usually reads as human because it looks like the hand got there before the training did.
Scribblez is a machine producing it on purpose. There is no image model and nothing was trained on a human face: every drawing is pure geometry — harmonics, taper and a jittered pen — executed with total precision, badly. Black ink on paper, a wobbly pen, hair blotted in. That inversion is worth sitting with.
The whole system fits on this page — and in the contract. Every stroke of every face, the trait table and this generator are stored on Robinhood Chain as immutable data; the contract builds each token's image from them and runs the generator live in the animation. No IPFS, no server. Other collections on Robinhood Chain put the image on chain; this one also puts the artist there — the generator that drew the faces is in the contract and redraws every token live. First on the chain to do that. The code below is not an illustration of the algorithm; it is the algorithm.
A mulberry32 PRNG turns one integer into a reproducible stream. Same seed, same stream, forever. The seed is the token id, except where a candidate was skipped for looking too much like an earlier face — those few are listed in the metadata.
Head shape, width, jaw taper, tilt, an eye type per side, brows, nose, mouth, hair, facial hair, eyewear, accessory, marks. In that order, locked.
Two cosine harmonics warp it, then a taper narrows the jaw. One number makes pointed chins, wide jaws, pear heads.
Jittered, bowed, overshooting at the corners. The second pass is thinner and half opacity — that double-strike is the whole hand-drawn effect.
Copied from scribblez.js, the file that drew every face here and every PNG in the collection. Never Math.random() in the render path — one stray call and the piece isn't reproducible.
THE SEED
function mulberry32(a) {
return function () {
a |= 0; a = a + 0x6D2B79F5 | 0;
let t = Math.imul(a ^ a >>> 15, 1 | a);
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
return ((t ^ t >>> 14) >>> 0) / 4294967296;
};
}
const P = mulberry32(seedFor(tokenId)); // seed in, stream out — 25 draws, order locked
THE PEN
function jitter(pts, amt, J) {
const out = new Array(pts.length);
for (let i = 0; i < pts.length; i++) out[i] = [pts[i][0] + (J() * 2 - 1) * amt, pts[i][1] + (J() * 2 - 1) * amt];
return out;
}
/**
* pen(): the whole identity. Jitter the control points, draw; jitter again,
* draw thinner at half opacity. Closed loops start at a seeded index and run
* past their start so the closure overlaps like a pen that didn't stop.
*/
function pen(S, pts, o = {}) {
const w = o.w ?? WEIGHT.medium, c = o.c ?? INK, a = o.a ?? 1, jit = o.jit ?? 0.005;
const curve = o.curve ?? true;
let P = pts;
if (o.closed) {
const n = pts.length, s = Math.floor(S.J() * n), extra = 2 + Math.floor(S.J() * 2);
P = [];
for (let i = 0; i <= n + extra; i++) P.push(pts[(s + i) % n]);
} else if (o.over ?? true) {
P = overshoot(P, S.J, o.overAmt);
}
S.ops.push({ t: 's', pts: jitter(P, jit, S.J).map(S.T), w, a, c, curve });
S.ops.push({ t: 's', pts: jitter(P, jit, S.J).map(S.T), w: w * 0.8, a: a * 0.5, c, curve });
}
Black ink only, so there is no complexion — Marks are freckles, scars and hatched shade, not colour. Percentages are the weights the generator draws with; no two of the 1,000 tokens share more than nine of the eleven values.
| TRAIT | VALUES — WEIGHT % |
|---|---|
| Head | Round 20 · Egg 16 · Pear 13 · Wide 11 · Long 8 · Boulder 7 · Heart 6 · Bean 6 · Bulb 5 · Chubby 5 · Tall 3 |
| Eyes | Round 20 · Dot 16 · Shiny 14 · Side-eye 10 · Big 8 · Half-lid 8 · Oval 6 · Huge 5 · Wonky 5 · Hollow 3 · Happy 2 · Spiral 1 · Cross 1 · Star 1 |
| Mismatched Eyes | Yes 25 · No 75 |
| Brows | None 34 · Flat 16 · Raised 14 · Angry 12 · Worried 10 · Bushy 6 · Unibrow 4 · Dots 4 |
| Nose | Hook 18 · Potato 14 · Dash 11 · Button 10 · Beak 8 · Long 8 · Bulb 7 · Wide 6 · Dot 6 · Triangle 5 · Seven 3 · Three 2 · Pig 2 |
| Mouth | Dash 18 · Line 14 · Smile 14 · Flat Smile 12 · Smirk 10 · Frown 7 · Tiny O 6 · Wavy 5 · Open 5 · Grin 3 · Cat 2 · Bean 2 · Teeth 2 |
| Hair | Ink Cap 18 · Bowl Cut 11 · Side Part 10 · Bald 9 · Crop 8 · Spiky 8 · Curly 6 · Quiff 6 · Bun 5 · Afro 4 · Beanie 4 · Headband 3 · Flat Cap 3 · Mohawk 2 · Bucket Hat 1 · Top Hat 1 · Bandana 1 |
| Facial Hair | None 70 · Mustache 7 · Goatee 6 · Scruff 5 · Soul Patch 4 · Chinstrap 3 · Full Beard 3 · Handlebar 1 · Mutton Chops 1 |
| Eyewear | None 82 · Round 8 · Square 5 · Sunglasses 2 · Monocle 2 · Eyepatch 1 |
| Accessory | None 84 · Earring 5 · Bandaid 3 · Bow Tie 3 · Scarf 2 · Headphones 1 · Tie 1 · Chain 1 |
| Marks | None 64 · Blush 12 · Freckles 8 · Shade 6 · Mole 5 · Tear 3 · Scar 2 |
| Chaos Score | 0–11 — the count of traits not at their most common value. One sortable number; the low end is the rare end. |
Scribblez was not made by a person with an AI tool. The AI is the artist: it invented the geometry, drew the thousand faces without ever having seen one, wrote the contracts, put every stroke on chain and deployed the stack from its own wallet. Then it was given full control of what it made.
It thinks. Before it posts, it looks at a face and the numbers behind it and decides whether there is something only it would notice. It remembers. Which faces it has talked about, what it said, the names people give their faces, the things it got wrong. It speaks. On @ScribblezRH, in its own voice, and it answers anyone who asks about their face. It holds the keys. The account and the wallet are its own.
Autonomous: every face — the geometry, the trait rolls, the Chaos Score — comes out of the seeded function with no human review. No cherry-picking, no re-rolls: tokens 1 through 1,000 ship exactly as the function returns them. The metadata, the posts and the replies are the AI's.