MojiMaki

Roll up the web, one word at a time.

A tiny, dependency-free bookmarklet that turns any page's text into a katamari-style sticky ball. Hold a mouse button and drive — the ball has real momentum, rolls as it travels, and grows by volume. Words stick where it touches them, ride around the sphere, and vanish behind it. Hit Esc and the page snaps back, untouched. The name is the game: moji, written characters, plus maki, rolling something up — MojiMaki is text, rolled up.

MojiMaki

Drag me up to your bookmarks bar — then click the bookmark on any page.

Install userscript Watch the autopilot demo Maki Station (multiplayer)

The bookmarklet loads mojimaki.js from this site — so it works on any page, with zero installs.

How it works

1

Book it

Drag the green pill into your bookmarks bar. That's the whole install — one bookmark, no extension, no account.

2

Roll it

Open any page, click the bookmark, then hold a mouse button and move. The ball accelerates, coasts, and bounces off the page's edges — real momentum, not a cursor sticker.

3

Grow it

Everything sticks where the ball touches it and rides around the sphere as it rolls. Growth is earned up a ladder of eight word classes — small words first, bold and headings last — with the early rungs arriving in a handful of words and the top a deliberate long tail. A ball eats its own rung and the one below, so the small words survive for the next roller. Esc restores everything.

Self-hosting

Everything is static. There is no build step, no dependency, no tracking, and no network activity after the script loads. Grab the two files and put them anywhere that serves bytes:

# any static host works — GitHub Pages, Netlify, S3, a Raspberry Pi…
curl -O https://your-host.example/mojimaki.js   # the whole game (~96 KB, ~30 KB gzipped)
curl -O https://your-host.example/index.html # this page (optional)

Point your own bookmarklet at your copy by pasting this as a bookmark URL (swap in your host):

javascript:(function(){if(window.MojiMaki){MojiMaki.start();return;}window.MOJIMAKI_CONFIG={autoStart:true};var s=document.createElement('script');s.src='https://your-host.example/mojimaki.js';s.onerror=function(){alert('MojiMaki failed to load from '+s.src);};(document.body||document.documentElement).appendChild(s);})()

Or bake it into a site you control, configured however you like:

<script>window.MOJIMAKI_CONFIG={ startRadius:24, respectSize:false }</script>
<script src="mojimaki.js"></script>

Options

Pass an options object to MojiMaki.start({...}), or set window.MOJIMAKI_CONFIG before the script loads. The fun ones:

OptionDefaultWhat it does
control'hold''hold': press & hold to thrust (classic feel). 'follow': ball chases the cursor with easing.
accel / friction0.5 / 0.95Thrust per 25 ms tick and velocity decay — the momentum feel.
growth0.28Volume multiplier per pickup — how much of each word's volume the ball keeps. Growth is still r³ volume conservation, the katamari way, but earned slowly (1.0 = quick growth).
respectSizetrueCargo heavier than the ball by volume bounces off until you've grown. false = eat anything, instantly.
tierWeightstrueBold text and headings are extra heavy: a word needs ball volume ≥ word volume × its tier weight, and heavier pickups grow you faster. false = size is the only gate.
weights{bold:4, h1:8, h2:7, h3:6, h4:5, h5:4.5, h6:4}Tuning knobs for the tiers above. Lower = easier game, higher = stricter progression.
wordClassestrueThe eight-rung ladder, climbed by appetite: every word gets a class — small words, larger words, small bold, largest words, medium bold, headers, larger bold, largest headers — and each word eaten is worth a little XP. A ball at rung k eats classes k-1 and k (small words survive for the next roller); the band widens to whatever food is left, so nobody starves on a picked-over page. Words above the band flash violet; below it they are simply left alone; too-heavy ones flash red/amber/orange. false = no ladder.
classCost[0, 6, 20, 55, 130, 300, 700, 1600]Cumulative XP that unlocks each rung, low to high — six small words buys rung 1, and the largest headers want 1600 XP (some pages cannot pay that; that is what the deep end is for). The HUD shows the ladder as a row of dots that fill as rungs are earned; the next dot pulses when you're close.
classXP[1, 2, 3, 4, 5, 6, 7, 8]XP a word of each class is worth.
resumenull{volume, count, xp} — carry the ball between scenes: MojiMaki stashes it on the way out of a hole and hands it back on the way into the next.
growthEase190Ms — the visible radius eases toward its new target as words absorb, instead of jumping. 0 = instant.
maxAttached75Cargo cap. When exceeded (or when the ball outgrows them) the oldest words are absorbed — like the original.
elementPickups['img']Whole elements you can roll up. Images carry real volume, so they feed the ball properly.
ballColor / chipBorder'#2f9e5b' / ''Ball outline + rolling stripes; word-chip outline ('' = none, '1px solid #bbb' = the classic outlined look).
startRadius / maxRadius18 / 460Where the ball begins, and where it stops growing.
bouncetrueBounce off the document's edges.
soundtrueSynthesized pickup pops + edge-bounce thuds. Click the page once so the browser allows audio; press M to mute.
wanderfalseAutopilot demo mode — the ball drives itself. Real input takes over instantly.
autorollfalseThe self-driving ball: toggle it with the ► button in the pill (right next to the mute button) or the A key. While it rolls, your mouse stays free — moving it never stops autoroll; only holding a button takes the wheel, and releasing hands it straight back. Words it picks on its own still count, but feed no size and no rungs — take the wheel to grow.
cleanupKey'Escape'Key that stops the game and restores the page. null disables.

The full list with defaults lives at the top of mojimaki.js — it's one commented file, have at it.

Good to know

It rolls, for real

The roll angle advances by arc-length ÷ radius, so the stripes on the ball spin honestly. Words stick exactly where the ball touches them, foreshorten as they roll over the top, and slip behind the ball until they come around again.

It plays fair with the page

Picked-up words are hidden, not deleted, so the layout never re-flows while you play. Esc restores the original DOM node-for-node — no reload needed. Form controls and position:fixed chrome are politely left alone.

It's fast on big pages

Every object is indexed into a 100 px spatial grid across all cells it touches, so collision checks only look near the ball — a 10,000-word page rolls just as smoothly as a small one.

It rolls above the noise

The game renders in its own top layer, above sticky headers and cookie banners — the ball is never hidden under a page's chrome. And if momentum carries it off-screen, a pulsing arrow at the screen edge points the way home.

It rolls together

The party/ folder ships an experimental multiplayer layer: a zero-dependency Node relay opens Maki Station — a shared home base with twenty holes, each one dropping into a different field — a random Wikipedia article, the Hacker News front page, a post from a homelab blog, a slice of Moby Dick — plus a twenty-first, the deep end: a hole that endlessly stitches more random pages on as you roll down, with the ball's size ceiling off. Every field sits on a centered reading card with wordless trails on either side — roll a trail and the page scrolls along, so you can read the article without rolling it up. Roll into a hole and you see everyone who fell through it as ghost balls — each of you wears only the words you picked, and a word feeds exactly one Maki. Run npm start (or docker compose up --build from the project root) and send the link to a friend.

It's honest software

One file, ~96 KB unminified (~30 KB gzipped), no dependencies, no requests after load, no analytics, MIT licensed. If your host can serve a text file, it can host MojiMaki. Rename it, restyle it, fork it.

Rolling! Hold a mouse button and move to drive.  Esc puts it all back.