Inspiration

Carpet-cleaning videos are one of the most-watched things on the internet, and nobody quite knows why. A filthy rug, a wand, twenty minutes, and millions of views. We wanted that feeling on a phone — but when we played the cleaning games that already exist, every one of them faked it. One swipe, one layer, and the dirt just vanishes. The thing that makes the videos hypnotic is that cleaning a carpet is a procedure: you vacuum, you wet it, you foam it, you agitate it, you pull the filth back out. Skipping to the payoff throws away the payoff.

Then we noticed the second half of the loop was sitting right there in the source material. The people making those videos aren't just cleaners — they're channels. The grimmer the carpet, the better the video. So the filth isn't just work. It's content.

What it does

You run a carpet-cleaning workshop and film every job for your channel.

Cleaning is the real procedure, and every tool asks something different of your thumb. You vacuum, and the rug looks worse — lifting the pale dust haze reveals the ground-in filth underneath instead of faking progress. You lay water, foam it with soap, then brush; the foam is hundreds of real spheres your brush shoves around, turning brown as they drink the dirt out of the pile. You squeegee the dirty foam off in long pulls and suction it dry. Gum is tapped away. Matted pet hair only comes out swiped along its grain. Set-in stains need a press and hold.

All of it is being filmed. Filth and long unbroken strokes make better video, video makes subscribers, and money goes back into a workshop you can see: the extraction machine, the camera rig, the tool rack, the bench, the crew bays. Tap the machine

There are no menus and no day counter. A carpet is always in the bay. The first is a bath mat you finish in twelve seconds; later it's a hoarder's front room that needs all nine stages. Finish one and the payday flies up over your counters while ng.

How we built it

Three.js and plain HTML5, single-player, portrait, no build step at runtime — a judge can unzip it and double-click index.html.

  • The dirt is not a texture, it's a simulation. Three WebGLRenderTargets sized to the exact pixel grid hold dust, embedded filth, loosened filth, water, soap and brush marks in separate channels. Cleaning draws quads with subtractive blending. Nothframe. masked stamps. Because additive blending scales by src.rgb, a pure-red stamp only moves the red channel — so three targets do the work of six, at four texture samples instead of six.
  • The foam is geometry, not a shader trick: 820 spheres in one InstancedMesh that the brush shoves, that soak up dirt, and that the squeegee scrapes off.
  • Everything is generated in code — meshes, textures, the whole audio kit — except one icon sheet.
  • A Puppeteer harness plays real gestures with a real mouse and checks 18 behaviours: that a common carpet is a job of seconds, that rarity climbs, that specialist muck actually clears, that no request leaves the machine.

The whole thing was built by prompting Claude Code (Claude Opus 5) — architecture, game code, procedural art and audio, the test harness, and the packaging. The icon sheet was generated with an image model and then detected and repacked into an exact grid by a script, because models lay icons out roughly and CSS needs precision.

Challenges we ran into

Our own test harness lied to us, twice. We lost most of a session to render targets that read back as zeros — the bug was --use-gl=swiftshader, which silently discards render target contents between frames. The game was fine the whole time. Later, the harness cheerfully reported "5 of 5 upgrade buttons on screen" while not one of them could be tapped, because a full-bleed overlay was swallowing every touch. It had only checked whether they were visible. Visibility is not reachability.

The first playable spike wasn't fun, and we recorded that verdict rather r tools used the same drag, so they were one tool with four names. That'swhere the rule "every tool gets its own verb" came from.

Portrait is only eleven degrees wide. At a normal field of view on a 412×892 screen, a room 24 units across is almost entirely off-camera, and four of our five upgrade buttons projected into the void. We stopped guessing and wrote a tool that projects candidate world positions onto a phone screen using the same maths the renderer does.

The foam took three failed attempts — a hard checkerboard, then salt-and-pepper static — before we worked out the real problem: we were drawing bubbles on the rug's 88×132 pixel grid, where each one was about three pixels across.

And the honest one: our carpets took two minutes each, roughly three times too long for the format.

Accomplishments that we're proud of

  • The vacuum makes it look dirtier. Dust and ground-in filth are separate layers, so lifting the haze reveals the mess instead of hiding it. It's the single change that gave every later stage something to pay off.
  • Foam that behaves like foam — real spheres that pile where you brush them, visibly turn brown as they pull dirt out of the pile, and get scraped away dirt and all.
  • Two minutes became twelve seconds. Rarity now scales carpet size, filth, stage count and pay together, so the first thing anyone touches is quick and finishes.
  • We deleted a screen. The payday used to be a page with a confirm button, in exchange for information you already had — you watched the rug get clean. Now the reward flies out of the counter it lands in and the next carpet is already in the bay.
  • 26fps → 64fps while tripling the mess, by turning 76 individual meshes into 3 instanced ones and killing a geometry rebuild that was running every frame.
  • 377 KB packaged — about 1% of the size limit — with zero network requests.

What we learned

A green test suite measuring the wrong thing is worse than no test suite.from a harness that was confidently wrong, and the fix each time was to

"Not really fun to play" was the most useful sentence of the project. It arrived early, about a throwaway spike built specifically to be thrown away, and it saved us from building an economy on top of a verb that didn't work.

Cutting scored better than adding. We dropped a roguelite card-draft system, a job-selection menu, a day counter and a results screen. Every one of those removals made the game read faster.

Silent failure is the expensive kind. A patch that does nothing, a shader that fails to compile, a replace that finds no match — all of them look exactly like success until something distant breaks.

What's next for Carpet Cleaning : Content Creator

  • The client in the room, watching you work and reacting to it.
  • Weather and venue — outdoor jobs where rain fights your drying time and the forecast is a real decision.
  • A channel that answers back: comments that steer which jobs go viral, so your audience starts shaping which carpets you take.
  • A co-op bay where two players work opposite ends of one enormous carpet.
  • Nearer term: a real iPhone in real hands. Everything above is downstreaore doesn't change — filthy thing, clean thing, and a workshop that growsaround you.

Built With

Share this project:

Updates

Submission history