This is a submission for the Hacktoberfest Weekend Challenge: Build for a Friend
Live: https://one-step-jypm.onrender.com · Chrome or Edge on a computer (WebGPU)
What I Built
My sister has a lot going on in her head at once: bills, a CV to redo, messages she hasn't answered, a trip to plan. Everything feels equally loud. When that happens, she doesn't pick the wrong task. She picks none, or she picks whatever feels most urgent, which is rarely what matters most.
So I built One step. You dump everything on your mind into one box, in any order, no structure. A small open-weight model reads it and gives back:
- one thing to do right now, small enough to start in under 10 minutes,
- at most three things for later, with anything that has a deadline first,
- and a single line: "Everything else can wait."
If the one thing still feels too big, there's a "Still too big" button that breaks it into an even smaller first step.
That's it. No accounts, no streaks, no reminders, no red badges. Everything about it is designed to lower the volume, not add to it.
What she said
I handed it over on Sunday evening, on a video call. She opened the link in Chrome, typed her own list, and I didn't help. These are her words, translated from French:
"It's already great, honestly."
"For prioritizing, it's really nice."
Then she said the thing that made the whole weekend worth it: she tends to do what's urgent before what's important, and urgent isn't always important. Seeing just one thing on the screen helped her see the difference.
She also immediately told me what she wanted next. More on that below, because it's the most useful feedback I got.
Demo
Live: https://one-step-jypm.onrender.com
The first visit downloads the model once (about 1.5 GB). After that it's cached, and every answer comes back in about a second. You can switch between French and English with the toggle at the top; she uses it in French.
Code
One step · Un pas
Dump everything on your mind; an open-weight model (Gemma 2 2B) running in your browser picks one thing to do right now. Nothing you write leaves your computer: no server, no API key, no account. Built for my sister for the Hacktoberfest 2026 DEV Weekend Challenge.
Try it: https://one-step-jypm.onrender.com · Needs Chrome or Edge on a computer (WebGPU). First load downloads the model once (~1.5 GB), then it's instant. Run locally: python3 -m http.server 8000.
How I Built It
One static HTML file. No framework, no backend, no build step.
-
Model: Gemma 2 2B (
gemma-2-2b-it-q4f16_1-MLC) - Inference: WebLLM on WebGPU, so the model runs on the GPU of whoever opens the page
- Output: WebLLM's JSON mode with a schema, so the interface always gets the same shape back
- Hosting: a static site on Render
Why this model
I first wanted the newest, smallest Gemma. But my sister lives far from me, so whatever I built had to work on her laptop from a link, with nothing to install, on the first try, tonight. Gemma 4 E2B runs in the browser too, but it's around 2.9 GB to download. Gemma 2 2B is officially prepared for WebLLM, about half the size, and good enough for this job. I picked the reliable option.
Measured, not guessed
| My laptop | Her laptop | |
|---|---|---|
| Model ready | not measured | 20.4 s |
| Time per answer | 0.9 to 1.5 s | 4.5 s for the first, then 0.9 to 1.1 s |
| Speed | 66 to 75 tokens/s | 30 to 38 tokens/s |
Her machine is about half as fast as mine, and it doesn't matter: one second is instant for a human.
What broke
1. The page sat on "Thinking…" forever. Two bugs at once. My call to the model had no try/catch, so errors went silently to the console. And I hadn't capped max_tokens, so a small model in JSON mode could loop on whitespace for minutes. I added streaming, a token cap, and visible errors. Then the real error showed up:
Failed to initialize the grammar matcher for response format `json_object`:
Cannot pass non-string to std::string
WebLLM wants the JSON schema passed as a string, not an object. One JSON.stringify later, the model was forced to return my exact shape.
2. The model was fast, and wrong. My first version asked for three lists: now, later, and "can wait". On my first test it put 4 items in a list capped at 3, invented a task I never wrote, gave advice about someone's mother, and put the same message in both "do now" and "can wait". On the second test it dropped the only task with a deadline, and copied "do the dishes" from my example into the answer.
What fixed it:
- strict rules in the prompt: only use tasks from the list, invent nothing, each task appears once;
- every action written as a short verb-first phrase, which also stopped it from mixing "you", "I" and formal French;
- deadlines first;
- an example with zero overlap with real lists, so there's nothing to copy;
- and the biggest one: I deleted the third list. "Everything else can wait" is now fixed text in the interface. The model can't get it wrong because the model doesn't write it.
Small model, smaller job. That was the lesson.
3. A blank page. When I added the language toggle, I moved all the text into JavaScript. But the script started with a static import of WebLLM from a CDN, and the browser runs nothing until that import finishes. So no text at all. I switched to a dynamic import() that runs after the text is on screen.
4. "Still too big" has a floor. On her laptop, she pressed it twice in a row: Pay the bill → Open the bill's page → Open the browser. It made us both laugh. But honestly, for someone frozen in front of a task, "open the browser" might be exactly the right size.
What she asked for next
- Tick a task and move to the next one. She wants the satisfaction of checking it off. That's a reward, not organization.
- See everything she has already done.
- Add tasks, and see the ones that disappeared. Some things she wrote weren't on screen anymore.
Number 3 is a direct consequence of my own design choice. Hiding everything except one task makes the screen calmer, but it also made her wonder where her tasks went. Calm versus trust is the real design problem here, and I only found it because a real person used it.
Why Does Open Innovation Matter?
Look at what she types into this box: bills, money, job worries, family. That's exactly the kind of text you shouldn't have to send to someone else's server to get help with.
With an open-weight model running in the browser:
- 0 bytes of what she writes leave her laptop. The page downloads code and model weights; her text never goes anywhere.
- €0 per use. No API key, no subscription, no bill for my sister or for me.
- It works offline once the model is cached.
- I could choose the model size to fit a laptop GPU, and swap it later without asking anyone.
A closed API would have meant sending her most anxious thoughts to a third party, paying per request, and needing a connection. For a gift, that's the wrong deal.
The rest of the stack is open too: WebLLM and MLC, and the Gemma weights that make any of this possible.
The honest part
This is a v0 built in one evening. It's a single HTML file of about 450 lines, styles included. It only works in Chrome or Edge on a computer, because it depends on WebGPU and fp16 shaders. It doesn't save anything between visits yet. The next version is already written for me, by her: tick, see progress, never lose a task. All of it can stay on her laptop.
Prize Categories
Gemma: the whole tool is Gemma 2 2B running locally in the browser through WebLLM on WebGPU, with JSON-schema-constrained output.

Top comments (0)