Inspiration
This idea started with me waiting. On Monday I opened my coding agent and typed, word for word: "yo yk how we were waiting for hacktober dev.to week 3 topic. why were we waityinh?" I had been refreshing the challenge page for the new theme, and before that the Week 2 page for results, and before that the petrol price board. I spend a silly amount of my week checking pages for one sentence that isn't there yet.
What it does
Wait For It is a web page where you type what you're waiting for in plain words, add a link if you have one, and then stop checking. It looks for you, rarely and on purpose:
- You write "the Week 2 winners are announced" with the challenge link, or "پاکستان میں پٹرول کی قیمت بدل جائے" (tell me when the petrol price changes in Pakistan) with no link at all.
- Gemma 4, an open-weight model, reads the page (or the past week's Google results) and answers two questions: has it happened, and when should I look next?
- When it has happened, or the date moved, you get a browser notification and a short voice note, in English or Urdu.
- The rest of the time it stays quiet. A watch for something a month away gets looked at once a week, not every hour.
Who gets to be lazier: anyone waiting on a page that will change once. Students waiting for results, people waiting for a price to drop, developers waiting for a release, and me, waiting for hackathon results. The effort it saves is the refreshing and the second-guessing, which for me is many small checks a day.
Try it: the live site | the code | the video
The live page is at https://waitforit.onrender.com (free Render instance, so the first load can take up to a minute while it wakes up). Notifications need you to click "Turn on notifications" once.
What a first check looks like. I typed "Linux 7.4 is released" and the kernel.org link, and Gemma's first look came back in about 5 seconds:

The real watches I left running overnight, including two that answer in Urdu. Each card shows what Gemma found, when it will look again, and the expected date it pulled from the page:

How I built it

The architecture is deliberately boring, because the interesting part is the decision in the middle:
- A free pinger (cron-job.org) calls
/api/tickevery 30 minutes with a secret key. That wakes the free Render server, which runs only the watches whose next check is due, and goes back to sleep. Nothing has to stay on, including my PC. - For a watch with a link, the server fetches the page, strips menus, footers and hidden HTML, and hashes the text. If the hash matches last time, nothing could have changed, so Gemma isn't asked at all.
- For a watch without a link, it asks SerpApi for the past week's Google results instead. Gemma turns the person's sentence into a short query once, when the watch is created, because searching the literal Urdu sentence found nothing.
- Gemma 4 (26B A4B, the open-weight mixture-of-experts model, hosted on Google AI Studio) gets the request, today's date, what it told the person last time, and the page. It replies with JSON: a status (happened, changed, delayed or nothing), a short summary in the person's language, the earliest and latest likely dates, how many hours until it should look again, and a quote.
- MongoDB Atlas holds the watches, their last 50 checks, the browser push subscriptions, the voice notes and the daily counters.
- ElevenLabs turns the summary into a voice note (Flash v2.5 handles Urdu) only when something happened or was delayed, never for "nothing".
The model never gets the last word
The first version trusted Gemma's status. That is exactly what keyword alerts get wrong in the other direction: they trust the page. So now Gemma has to show its work. For any news it must copy a few words from the page, and code checks that those words are really there before anyone is told:
// A quote that skips words ("India were champions... beat New Zealand") counts if every piece is there.
export function quoted(quote, text) {
const norm = (s) => String(s).toLowerCase().normalize('NFKC').replace(/\p{M}/gu, '').replace(/[^\p{L}\p{N}]+/gu, ' ').trim();
const t = norm(text);
const pieces = String(quote).split(/\.{3}|…/).map(norm).filter(Boolean);
// Long enough to mean something: two words, or a number like a version ("158.0").
const enough = (q) => q.includes(' ') && (q.length >= 6 || (q.length >= 4 && /\d/.test(q)));
if (!pieces.length || !pieces.every(enough)) return false;
return pieces.every((q) => ` ${t} `.includes(` ${q} `) || (q.length >= 12 && t.replace(/ /g, '').includes(q.replace(/ /g, ''))));
}
If the quote isn't on the page, the status is downgraded to "nothing", the check is saved with the rejected quote for me to look at, and no notification or voice note goes out. The same idea runs through the rest of the code:
- Gemma picks when to look next, but code clamps it to between 30 minutes and 7 days, and forces a look at least every 6 hours once the earliest likely date is within 3 days. In the eval Gemma once asked to sleep for 720 hours (the 2027 Cricket World Cup); the code cut that to a week.
- Gemma can say a watch is done, but for anything that happens again ("tell me when the price changes"), code ignores that and keeps watching. I added this after the petrol watch found a two-day-old price cut on its first look and switched itself off.

Every key has a hard daily cap stored in the database, because I am a student on free tiers:
| Per check | Typical | Daily cap in code |
|---|---|---|
| Gemma call, page watch | 10,383 tokens in, 122 out, about 4 s | 400 calls |
| Gemma call, search watch | about 930 tokens in, 80 out | (same 400) |
| Gemma call when the page is unchanged | 0 (skipped by the hash) | n/a |
| SerpApi search, no-link watch only | 1 search | 8 searches |
| ElevenLabs voice note, news only | about 60 characters, 3 s of audio | 15 notes |
On top of that: at most 5 watches per person, 60 in total, 10 new watches per IP per day, and every watch ends after 45 days. I'll delete the keys when judging is over.
Plain Node (no framework), about 900 lines, MIT licensed.
What I measured
A watcher is only as good as its "has it happened?" call, so I built a test set before writing this up. I picked 36 real pages on October 21, wrote a question for each, and labelled every answer by reading the page itself, separately from Gemma:
- 15 release pages (kernel.org, python.org, nodejs.org, go.dev, the Rust blog, Deno's GitHub releases, Firefox release notes), mostly in pairs on the same page: "Linux 7.3 is released" (yes) and "Linux 7.4 is released" (not yet).
- 21 Wikipedia pages for events: the 2026 World Cup final (happened), the 2030 World Cup (not yet), GTA 6 (scheduled for November 19), the Nobel Peace Prize winner (announced) and its ceremony (December 10, not yet), and so on.
- 19 have happened and 17 haven't. The "not yet" pages are the hard ones, because they are full of words like released, announced and champion.
Each page is saved once, so every run reads the same text, and node eval/run.mjs runs the real check (the same checkWatch the server uses, including the quote check) next to a keyword rule that fires when a done-sounding word is on the page. That rule is what a "tell me when this page changes" alert effectively does.

| 36 pages | Correct | False alarms | Missed |
|---|---|---|---|
| Keyword rule | 18 | 15 | 3 |
| Gemma 4 + quote check | 35 | 0 | 1 |
Challenges
All of these are real, and most of them changed the code:
- The one miss is Artemis II. The page says it launched on April 1, 2026 and landed April 11. Gemma read "launch date April 1, 2026" as a plan and said it hadn't happened, even though today's date (October 21) was in the prompt. Dates in the past written like schedules are still a weak spot.
- My first full run scored 19 of 36, and 16 of those failures were mine: the free tier limits tokens per minute, and long pages (8,000 to 14,000 tokens each) hit it after two calls. The app now waits as long as Google's error says and carries on, which means a run takes about a minute per page.
- One label was wrong, and it was my fault. I marked "Firefox 158 is out" as not yet because my search pattern missed it; Gemma said it was out, and 158.0 and 158.0.1 are both on the page. I fixed the label and added Firefox 159 as the "not yet" case.
- The quote check was too strict twice: it refused "158.0" (too short) and a quote with "..." joining two sentences from the T20 World Cup page. Both were true. Version numbers and quotes with gaps now count when every piece is on the page.
- The local version was quietly broken until I tested it properly. Ollama gives a model a 4,096-token context by default, so on a 10,000-token page local Gemma only ever saw the first 2,051 tokens and mostly answered with nothing. The Ollama call now asks for 12,288 tokens; 20,480 was too big for my 6 GB GPU and pushed half the model onto the CPU.
What it can't do
- Pages that only render with JavaScript, or sit behind a login, come back nearly empty. University portals are the obvious example. A headless browser would fix it, at a cost I haven't paid yet.
- It reads the first 14,000 characters of a page. If the news is at the bottom of a very long page, it won't see it.
- No-link watches depend on Google having the news in the past week, and on 8 searches a day for the whole site.
- The free server sleeps, so a check can run up to 30 minutes late. For exam results that's fine; for concert tickets it isn't.
- Notifications need the browser to have been allowed once, and iPhones only show web push for sites added to the home screen.
- The test set is 36 pages picked by one person on one day. It shows the idea works and where it breaks; it isn't a benchmark.
Accomplishments: the lazy payoff
I am using it for the things I'm actually waiting for. These are the five watches running on the live site right now, all started on October 22 at about 2:45 AM my time:
| Watch | First answer | Next look Gemma chose |
|---|---|---|
| The Week 2 winners are announced (the challenge page) | Expected during the week of October 19 | 6 hours (inside the window) |
| Petrol price in Pakistan changes (Urdu, no link) | No new change since the last cut | 24 hours |
| Linux 7.4 is released (kernel.org) | 7.3 is the latest, 7.4 not yet | 24 hours |
| GTA 6 comes out (Wikipedia) | Still November 19 | 7 days |
| Python 3.16 is released (python.org, Urdu) | Not yet, expected around October 2027 | 7 days |
How much refreshing that replaces depends on how anxious you are, so here is the comparison against someone who checks three times a day, using the schedule rules above and assuming the dates on the pages hold:
| Watch | Until | Person checking 3 times a day | Wait For It's looks |
|---|---|---|---|
| GTA 6 | November 19 (28 days) | 84 | about 9: weekly, then every 6 hours in the last 3 days |
| Linux 7.4 | early December (about 45 days, no date on the page) | about 135 | about 45: daily, since it has no date |
| Python 3.16 | October 2027 | over 1,000 | about 60: weekly for a year, then every 6 hours |
| Week 2 winners | this week | 3 a day | 4 a day, and I don't have to do any of them |
And for every one of those looks where the page hasn't changed, Gemma isn't even asked. That is the whole point: the only looks these pages need from now on are Gemma's, and I can close the tabs.
What I learned
Make the model show its work. Gemma's answers only became trustworthy once it had to quote the page and code checked the quote. That one rule took false alarms from 15 (keyword alerts) to 0. The second lesson: test the local setup for real. Ollama's default context window quietly cut every page to its first 2,051 tokens, and I only found out by measuring.
What's next
- A headless browser for pages that only render with JavaScript, so university result portals work.
- Reading past the first 14,000 characters on long pages.
- A bigger test set, labelled by more than one person.
- Watches you can share, so a whole class can wait on one results page with one check.
Running it on your own PC
Because Gemma's weights are open, one setting (OLLAMA_URL) switches Wait For It to gemma4:e4b-it-qat on your own machine, so a private page never leaves your PC. On my 6 GB GTX 1660 Super it got 5 of 6 eval pages right, at 42 to 124 seconds a page against about 4 seconds hosted. A watcher that looks once a day can afford that.
Timeline: what existed before the event and what was built during it
- Before October 16 (pre-existing): nothing of Wait For It. The Gemma calling code is adapted from my earlier open-source project scamcheck (started October 19, also inside this event), credited in the README.
- During the event (October 20 to 22): all of it: the watcher, the quote check, the scheduling rules, the web page and push notifications, the Urdu voice notes, the 36-page eval, the Render and Atlas setup, and the video.
- Also entered in: DEV's Hacktoberfest Week 3 challenge (same project).
- Third-party: Gemma 4 (Google, open weights) on Google AI Studio or Ollama, SerpApi, ElevenLabs, MongoDB Atlas, Render, cron-job.org, Remotion and edge-tts for the video. AI assistance: built with Claude Code as my coding agent, which also helped draft this write-up; every number comes from the eval output in the repo or the live site.
Built With
- cron-job.org
- edge-tts
- elevenlabs
- gemma
- github
- google-ai-studio
- javascript
- mongodb
- mongodb-atlas
- node.js
- ollama
- playwright
- react
- remotion
- render
- serpapi
- service-worker
- web-push
Log in or sign up for Devpost to join the conversation.