Friendzone Buildathon 2026  ·  Decentraland Mobile
A blob raised by strangers
who never meet.

One creature in one Decentraland World. Every visible property of its body — its size, its dance, its plaque — was made by somebody else.

SDK7 · TypeScript react-ecs · Tween Node 22 · ws · SQLite $0.00 provider cost MIT · solo build
move #7 · taught by a stranger oldest first
The problem, stated once

Presence is the
only content.

And presence is scarce. Decentraland worlds are venues — wonderful when full, dead when empty, which is almost always.

00:00 06:00 12:00 18:00 24:00 you · 02:10 them · 15:40 13 hours apart. Under every existing design, neither one exists to the other.
Why the obvious fixes don't work

Every patch needs a
second person, right now.

A person arriving alone finds an empty couch, and nothing says other people were here and cared about this.
02:10, alone

Someone opens Decentraland on their phone at 2am because they can't sleep. The world they pick is a beautiful, empty room. They look around for eleven seconds and close the app.

This is not a content problem or a graphics problem. It is a scheduling problem, and it is the platform's own retention problem stated plainly: there is no reason to open a venue twice.

Source: specs/spec.md — the problem statement this project was scoped against.

What everybody tries
01

Schedule an event

Needs a crowd to show up at a fixed hour, in a fixed timezone.

02

Hire a host

Needs a paid human awake at 2am, and stops the moment the money does.

03

Add a chat box

Needs somebody on the other end to answer. An empty room has nobody.

All three fail identically: they require other people to be online at the same moment as you — exactly the thing an empty world cannot supply.

The solution

The creature is the log.

No counter beside the blob. No leaderboard. The record of who cared for it is the shape of its body.

Mochi is a giant pastel blob co-parented by every visitor. Feed it and it is physically bigger from then on — for everybody, forever, whether or not you are still online.

The growth rule, as shipped
scale = 1.000 + 0.012 × feeds  (max 1.9)

Server-authoritative. The scene owns no state, keeps no counter and has no delete verb — nothing anyone contributes can be taken back out.

↩

Two people who never meet still act on each other.

Live in this slide
press F to feed
size on arrival +0.012 the ledger — one mark per feeding, and no way to remove one
1.000
current scale
0
feedings, all-time
last fed by nobody
—

Real constants from src/config.ts. The plaque string is the one the scene prints.

Creativity & originality

Four ways a stranger stays.

Each one is a different grammar of credit — a quantity, an ordered list, a name with a timestamp, and a message addressed to you.
01

Its size is the sum

The blob is bigger because people fed it. Not a number beside it — the actual body. Hunger decays over 36 h to a floor of 0.15, so an untended Mochi looks untended, and can never starve.

perFeed 0.012 · base 1.0 · max 1.9 · floor 0.15
02

Its dance is a credited chain

Each move was taught by one named person. When Mochi performs, it replays the whole chain in order, crediting every move, beside ghost dancers wearing those people's real wearables.

replaying all 7 moves  →  the chain is yours now
03

Its plaque names the last human

Deliberately coarse. “3h ago” carries the feeling that somebody was here and has since gone; “2h 47m ago” reads like a log file and carries nothing.

last fed by Kito
3h ago
04

The away-line finds you

On your next arrival, Mochi names the person whose act followed yours. It is the one message in the whole protocol that is never broadcast — it is addressed to a single returning visitor.

Rue fed Mochi after you left.

Naming note: Kito and Rue are this repo's test-fixture wallets, shown here as the literal string format. They are not real carers — see slide 11 for the real count.

Sixty seconds, one thumb

The whole loop, on a phone.

No typing anywhere. No tutorial. No fail state. No buttons on screen at all — every verb is a tap on an object in the meadow.
Mochi is hungry
last fed by Kito
3h ago
1
FEED
TEACH
01 · arriveA name, and a gap
your visit is in the guestbook
last fed by you
just now
2
FEED
TEACH
02 · feedOne notch bigger, permanently
teach Mochi a move
wave
clap
dance
raise
pump
robot
kiss
shrug
dab
disco
whoa
tik
3
FEED
TEACH
03 · teach12 built-in moves, zero typing
replaying all 7 moves
move #7 of 7  ·  taught by you
4
FEED
TEACH
04 · the chain replaysThe finale is your move

Drawn from the shipped UI source (src/ui/hud.tsx, emote-picker.tsx, plaque.ts) — these are illustrations, not captures. The World is live — walk into it

How it works

The scene owns no state.

It sends intents and renders what the server says. One process, one SQLite file, one creature — correct for that, and honest that it does not shard.
Decentraland Mobile · SDK7 scene
ui/hud.tsx
ui/emote-picker
mochi/creature.ts · aliveness.ts (Tween)
mochi/replay.ts · dancers.ts · plaque.ts
mochi/teach.ts · meadow.ts · ui/controls.ts
net/client.ts backoff 1s → 20s · intent queue capped at 8

Losing the connection never clears the screen.

lastState survives a disconnect on purpose, and no error notice is shown — it would only mark a gap the visitor cannot act on.

Zero imported GLB/GLTF. 26 Tween + 9 TweenSequence, zero Animator.

WebSocket · protocol.ts
intents ▸
hello · feed · teach{emoteId, wearables} · pet · stamp
◂ state
pet row · last 40 chain moves · today's carers

protocol.ts has zero imports — both sides import the same file, so client and server cannot disagree about what a message is.

Rate limits per wallet per 60s — feed 4 · teach 2 · pet 10 · stamp 2.
Guests render everything and are refused writes.

Node 22.5 · ws · Fly.io
ws.ts  —  socket + framing
game.ts  —  rules, limits, guests
store.ts  —  queries
node:sqlite — three tables
pet ×1 chain_move carer_event

append-only · teacher_name NOT NULL · no delete verb exists

Hunger is derived, never ticked.

It is computed from a timestamp, so there is no cron, and the value is correct after any amount of downtime.

Mobile-first · UX & accessibility

Designed for a thumb,
not shrunk to fit one.

Judged criterion #1, and first in the published tie-break order. Every decision below exists because the target device is a phone held in one hand.

No buttons, ever

FEED is the bowl, TEACH is the stage, PET is a hold on the body, STAMP is a tap on the totem. Device testing found a bottom thumb arc sitting on the client’s own joystick and jump control, so the arc was deleted, not nudged.

HUD is a status line — the bottom of the screen is the client’s

Zero typing, zero tutorial

Identity comes from the wallet. There is no onboarding modal and no instruction text, because judges arrive as players, not as readers.

No gesture can fail

Hold-to-pet latches on press and the release is watched globally, so a sliding thumb cannot cancel it. There is no fail state anywhere in the scene — a fat-thumb gesture that can fail is an accessibility defect.

tap + press only · no drag, pinch or traced shape

Authored at mobile scale

Sized in virtualWidth / virtualHeight, never raw pixels; desktop layout ×3 for mobile per Decentraland's own guidance; wrapped in ScreenInsetArea for notch and home indicator.

Accessibility floor

≥ 18sp in-scene text, ≥ 22sp on buttons
≥ 4.5:1 contrast on every text run
Nothing depends on sound — the mobile client has no audio events
No red/green-only signalling
No timed input, anywhere
Complete visit in 30–60s, one-handed, portrait
Crosshair hidden, joystick kept — the core loop survives standing still
Performance & execution

Every number is public.

One runtime dependency. No external API, no model, nothing on-chain — so there is no bill and nothing to expire before judging.
220
tests, 13 files
100% of statements, branches, functions, lines
1
runtime dependency
ws. SQLite is node:sqlite, built in.
$0.00
provider cost
Nothing to rate-limit, nothing to run dry mid-judging
78,482
combinations swept

One exhaustive test proves no reachable combination of feeding and elapsed time produces a creature that starves. 40,480 decay · 36,432 starvation refuted · 1,150 feeding · 420 no-punishment

Inside the mobile budget

payload    6.7 MB / 25 MB
entities   <250 / 4,800
triangles  <65k / 1,000k
draw calls <120 / 1,000

Every mesh is a procedural SDK7 primitive. Zero imported GLB/GLTF.

Shipped like production

5-stage CICodeQL gitleakssemantic-release MITconventional commits

github.com/edycutjong/mochi — read the tests before you believe the numbers.

Receipt from a real run
$ npm run demo:receipt
sessions      4
intents       6
wall clock    1,845 ms
database      4,096 bytes
✓ chain_move append-only invariant held
✓ hunger derived, no tick required
✓ guest session refused writes

Four sessions, two wallets, one database file smaller than a favicon. The whole social mechanism fits in 4 KB.

Reproduce: build/DEMO.md · measured on a 2026 MacBook
Your seven criteria

Built against the rubric,
line by line.

Unweighted, so the ranking is a sum and floors decide — one weak criterion costs exactly what a strong one gains. Nothing here was left to demo well and score badly.
Criterion What answers it Evidence
Mobile-First Experience tie-break 1 No buttons at all — four world-space taps, no typing, no tutorial, no fail state. Nothing is drawn over the client’s own controls. meadow.ts · hud.tsx · controls.ts
Social Value Cooperation without co-presence. Four credited traces — size, chain, plaque, away-line — each naming a specific other person. chain_move · carer_event
Mobile UX & Accessibility ≥18sp text, ≥4.5:1 contrast, no sound dependency, no timed input, one-handed portrait, notch-safe insets. ScreenInsetArea · ×3 scale
Performance & Optimization 6.7 MB of a 25 MB budget, under 250 entities of 4,800, zero imported models, dancers degrade 6 → 3 → name tags. 88–90% (S24 Ultra)
Creativity & Originality A creature whose body is an append-only log of strangers. The away-line is a message that waits for one person to come back. replay.ts · 12 signatures
Retention & Discovery tie-break 2 The reason to return is authored by somebody else while you were gone, and it is addressed to you by name. away-line, sent on hello
Overall Execution tie-break 3 220 tests at 100% coverage, 5-stage CI with CodeQL and gitleaks, one dependency, MIT, honest limitations documented in-repo. github.com/edycutjong/mochi
What isn't true yet

The honest slide.

This deck is going to be read next to a public repository. Everything below is already written down in JUDGE.md — you should hear it from me first.

Known limits, shipped

Emote observation is unverified on mobile

Catching a move performed with the client's own emote wheel is not confirmed on a phone. The 12-tile picker is the shipped fallback, so what is at risk is spontaneity, not the mechanic.

Audio is decorative

The Decentraland mobile client has no audio event implementation. Nothing in the experience depends on sound.

One process, one SQLite file

Correct for one creature and a few hundred carers. It does not shard, and it is not built to.

History was bootstrapped

Seeded rows carry an is_seed flag in the database and are labelled as such. The server refuses guest writes.

One axis of state

Hunger only — not a mood system. A deliberate cut, so the loop stays legible in eight seconds.

Not measured, not claimed

Decentraland World URL wunderland.dcl.eth
On-device score, mid-spec phone not measured
Real carers today 7

One of those seven is me. The other six arrived through the Decentraland community — one of them found the World unprompted — and between them the creature has been fed 86 times and carries a 42-move chain. Four of those people have taught a move that is now permanently credited to them by name: iOSWaifu, ginoct, abnerdune and Blaze Raveneye. Every row is a real wallet performing a real emote. It is more than a test fixture and still less than adoption, and I would rather report seven than dress it up.

The seeding plan

forward-looking targets

A drive with real Decentraland community members is under way — the seven carers above came from it. These remain the numbers I am holding myself to, and they are targets, not results:

≥ 30
carers
≥ 25
credited moves
≥ 8
wearable sets
≥ 4
days of spread

Checkpoint 2026-08-31. Whatever the real figure is on submission day, that is the figure the BUIDL will carry.

The ask

Visit twice, hours apart.

That is the entire test. If the second visit is different because of somebody you never met, the thesis holds. If it isn't, it doesn't.

For the jury

Open it on your phone, alone, at an odd hour — the condition it was built for. Feed it, teach it one move, close the app. Come back later and read what it says to you.

For DCL Regenesis Labs & Creator Success

What continued work buys, in order:

01

Seasonal chain resets that archive rather than delete — the dance becomes a history with chapters, and nobody's move is ever erased.

02

A second creature in a second World, so the two can exchange moves — chains that cross communities.

03

Mobile Discover featuring, if it earns it. This mechanic gets stronger with population — the rare case where distribution improves the product, not just its numbers.

All three are scoped from what already runs today — none of them needs a new platform capability, a new dependency, or a change to the mechanism.

Read the source
github.com/edycutjong/mochi

MIT. Tests, CI and the honest-limitations document are all in there.

Watch it work
youtu.be/UD4k311JT2k

2:27, captioned. The chain replay at 1:14 is the part that cannot be explained in writing.

Edy Cu — solo build

Scene, server, tests, CI and this deck. Friendzone Buildathon 2026.

Two people who never meet
still act on each other.

FRIENDZONE 2026 v1.15.1
01 / 12
← → navigate · ESC overview · P presenter · C contrast · F feed (slide 04)