Underground 2 Revived
Play Need for Speed: Underground 2 (2004) in Chrome and Firefox with WebGPU graphics, Web Audio, up to 120 fps widescreen, controller support, free roam and Online races with friends. Fan project, not
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Pain travels one way. Comfort travels back.
A game for couples who are apart. She writes how her period pain feels. His phone
starts buzzing and keeps buzzing. He never sees what she wrote, only short clues
from the AI, and he has to work out what she needs from a drawer of things he can
send. Only she can make it stop.
Built for the Elevate Women Global Hackathon.README 전문 보기 ↗Pain travels one way. Comfort travels back.
A game for couples who are apart. She writes how her period pain feels. His phone starts buzzing and keeps buzzing. He never sees what she wrote, only short clues from the AI, and he has to work out what she needs from a drawer of things he can send. Only she can make it stop.
| Live app | https://yuzu-vert.vercel.app |
| Try it with no sign-in | https://yuzu-vert.vercel.app/demo |
Built for the Elevate Women Global Hackathon.
Her screen is a small anime room. Every gift he sends appears around her, and the room warms up as her pain score falls to zero.
One Gemini call. Her sentence goes in, three things come out:
| Output | Why it needs AI |
|---|---|
| A vibration pattern | "Stabbing" and "dull ache" should feel different. Fixed buttons cannot do that. |
| Every need she mentioned | "I'm freezing, my back is killing me and I miss you" is three needs. His phone keeps going until all three are met. |
| Three clues per need | One for each guess he has. They make him think, rather than handing him the answer. |
Counting needs. The model first lists her separate complaints, then writes exactly one need per complaint. That stops it inventing a need she never asked for, or dropping one she did.
The three clues:
Checked in code, not just asked for. The rules are a system prompt, and her message is sent separately, so what she writes cannot change how the rules work. Every clue is then checked before he sees it. A clue that reads like an instruction, names the item he is looking at, or rules out something she actually needs is replaced.
Always works. Gemini runs only on the server, so the key never reaches a browser and her words never reach his phone early. Replies take about 1.3 seconds. If Gemini is down or has no key, built-in rules answer instead.
Code: lib/translate.ts (prompt, checks, fallback) and app/api/translate/route.ts.
/demo: no sign-in, no pairing. A whole room scans it, taps
once, and every phone buzzes with the same cramp. Every phone sends the same
message, so after the first one it is all cache: forty phones, one Gemini call./watch/CODE for filming and live demos: both phones side by
side on one screen, updating live. Read only. Sign in as either person to use it,
and add ?bare=1 for a clean screen recording.| Android (Chrome) | iPhone (Safari) | |
|---|---|---|
| Install as an app | ✅ Install button | ✅ Share → Add to Home Screen |
| Buzz while the app is open | ✅ Real vibration | 🔊 A soft sound instead |
| Buzz while the app is closed | ✅ Notification with our vibration pattern | ✅ Notification, iOS 16.4+, installed to the home screen |
| Our own vibration rhythm | ✅ | ❌ |
iPhones do not let websites control vibration at all. No browser on iOS supports the Vibration API, and iOS ignores the vibration pattern on notifications. On iPhone the notification buzz comes from iOS itself, so check Settings → Notifications → Yuzu → Sounds is on. For demos, use Android for his phone.
| Part | What we used |
|---|---|
| App | Next.js 16, React 19, TypeScript, Tailwind |
| Sign-in, database, live sync | Supabase: Google OAuth, Postgres with row-level security, Realtime broadcast |
| AI | Gemini (a model chain, tried in order), structured JSON output, system prompt |
| Notifications | Web Push with VAPID keys, sent at high urgency |
| Hosting | Vercel |
npm install
supabase/schema.sql: tables and security rulessupabase/002_join_and_names.sql: joining by code, and seeing your partner's namesupabase/003_push.sql: notification subscriptionssupabase/004_many_rooms.sql: several rooms, and only she can close onehttp://localhost:3000/auth/callback and https://YOUR-APP.vercel.app/auth/callback.anon key and the service_role key.cp .env.example .env.local
| Key | Where it comes from |
|---|---|
NEXT_PUBLIC_SUPABASE_URL | Supabase → Settings → API |
NEXT_PUBLIC_SUPABASE_ANON_KEY | Supabase → Settings → API |
SUPABASE_SERVICE_ROLE_KEY | Supabase → Settings → API. Server only. Never add NEXT_PUBLIC_ to it. |
GEMINI_API_KEY | Free at https://aistudio.google.com/apikey |
GEMINI_MODEL | Optional. Default: gemini-3.5-flash-lite,gemini-3.5-flash,gemini-flash-latest |
NEXT_PUBLIC_VAPID_PUBLIC_KEY | See below |
VAPID_PRIVATE_KEY | See below |
VAPID_SUBJECT | mailto: and your email |
Make the two VAPID keys:
node -e "console.log(require('web-push').generateVAPIDKeys())"
The service role key is how the server finds the other person's notification subscription. The security rules stop everyone else from reading it, so the route checks for itself that you are really in that room.
npm run dev
Open http://localhost:3000.
On the same wifi, find your laptop's address:
ipconfig getifaddr en0
Open http://<that-address>:3000 on both phones.
Notifications and installing need HTTPS, so test those on the deployed site.
npm i -g vercel
vercel --prod
In Vercel → Settings → Environment Variables, add all eight keys from the
table above, then deploy again. Also add https://YOUR-APP.vercel.app/auth/callback to
Supabase's redirect URLs and to your Google OAuth client. A missing redirect URL is the
most common reason sign-in breaks after deploying.
| Path | What it is |
|---|---|
app/room/[code]/page.tsx | The game: live sync, gifts, judging, hits, notifications |
components/HerScreen.tsx | Her screen: the room, the message box, judging |
components/HisScreen.tsx | His screen: the clue, the drawer |
components/RoomScene.tsx | The anime room |
lib/translate.ts | The AI prompt, the clue checks, the fallback rules, scoring |
app/api/translate/route.ts | Calls Gemini |
app/api/push/route.ts | Sends notifications |
public/sw.js | Service worker: shows notifications, caches only the icons |
lib/haptics.ts, lib/sound.ts | Vibration patterns, and every sound (all generated, no audio files) |
app/room/page.tsx | Her list of rooms, or his join screen |
app/watch/[code]/page.tsx | The spectator view |
supabase/ | Database setup, plus scripts to reset test accounts |
scripts/status.py | Who is signed up, who is paired, recent rounds |
prototype/index.html | The original one-file prototype. Open it with no setup. |
Everything in this repository was written during the Elevate Women Global Hackathon 2026. The git history is the evidence: 50 commits, all dated 16 to 19 September 2026, starting from an empty folder. No part of it existed before the hackathon, and nothing was carried over from an earlier project.
What we did not write is the usual off-the-shelf layer: the framework, the database, the model, and the host. Those are listed under licences below.
Inside the product. Google Gemini is the AI feature itself. It reads her
message and returns the vibration pattern, the list of what she needs and the
clues. It runs server-side only, in app/api/translate/route.ts, with the
prompt in lib/translate.ts.
While building it. This project was written with heavy use of Claude (Anthropic) as a coding assistant, through Claude Code. It was used for writing and refactoring application code, designing and testing the AI prompt, debugging (notably the iOS notification work), and drafting documentation. Every line was reviewed, run and tested by us, and we are responsible for all of it. Design decisions, the product idea, the game rules and the priorities are ours.
Everything we wrote is MIT licensed: see LICENSE. Third-party parts, all open-source and none restricting commercial use:
| Component | Licence |
|---|---|
| Next.js, React, Tailwind CSS | MIT |
@supabase/supabase-js, @supabase/ssr | MIT |
web-push | MPL-2.0 |
| Zen Maru Gothic, Zen Kaku Gothic New (Google Fonts) | SIL Open Font License 1.1 |
Build tooling pulled in by the above (sharp/libvips, caniuse-lite) | LGPL-3.0-or-later, CC-BY-4.0 |
Gemini, Supabase and Vercel are used through their normal free tiers, under their own terms. All artwork, including the anime room, the icons and the vibration and sound design, was made by us. No stock images or purchased assets are used, and emoji come from the reader's own device.
Yuzu stores personal data, including what she writes about her pain, which is health information. PRIVACY.md says exactly what is stored, who can read it, and how to delete it. In short: only the two people in a room can read it, enforced by the database and not just the app; her message never reaches his phone until the game reveals it; and there is no tracking or analytics of any kind.
If you are judging this: please use a test Google account.