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 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證A time-loop text adventure in FishE's village: the day resets at the bell, what you know does not. Built on tak.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗A day in a fishing village that will not end.
You wake on the docks at eight with a rod beside you. At nine in the evening a storm comes in off the water. At eleven the harbour bell rings — a bell that, Sam will tell you, has had no rope on it for thirty years — and you wake on the docks at eight.
Everything resets but what you know. What you know is the only way out.
Tidewater is a time-loop text adventure set in FishE's village, built on tak, the text-adventure kit extracted from it.
pip install -r requirements.txt needs git on your PATH: the kit is installed from its GitHub tag.
In your browser — the game runs in your tab, saves live in your browser:
pip install -r requirements.txt
python3 web/build_zip.py # once, and after any src/ change or tak upgrade
python3 web/serve.py # then open http://127.0.0.1:8080
In a terminal:
pip install -r requirements.txt
./run.sh # or: PYTHONPATH=src python3 -m tidewater
As a server (the browser is a terminal for a game running on your machine; everyone who opens the page shares it): ./run.sh web, then open http://127.0.0.1:8000. TIDEWATER_WEB_HOST/TIDEWATER_WEB_PORT move either server.
Docker: docker build -t tidewater . && docker run -p 8080:8080 tidewater serves the browser build.
A stranger, a village, one repeating day, and a thirty-year-old night nobody finished. The game tells it in pieces; the journal's "What is happening to you" page assembles the pieces you have found, in plain words. The whole of it, spoilers included, is in docs/STORY.md.
The day is fifteen hours long. Walking between places and reading the journal are free; everything else you do costs an hour — Ada's whole story costs two, and sleeping, or waiting for the storm or the bell, takes you straight to it. Seven places — the docks, Gilbert's shop, home, the tavern, the bank, the lighthouse on the point, the churchyard above the village — and six villagers who keep hours. Talk to people. Some of what they say is a fact, and facts survive the night: they appear in your journal and they open new questions on other villagers' menus. Facts also point at each other, Outer Wilds fashion: under each one the journal lists where it leads that you haven't been, without ever naming what is there. There are fifteen facts and two ways out — and a third way that is both. The trail to the bell is four facts long; the trail to the light — the lamp on the point, which went dark the night the Marigold was lost, and can be made to hold through the storm at nine — is five. Either can be walked in a single day once you know the way, and neither can be walked in the first. Each ending answers the questions the village has been refusing to: who cut the rope, and why a bell with no rope rings at eleven — Ada tells you in the lamp room, or Tom tells you in the tower. Each also tells you, plainly, that only half of that old night is mended. Do both in one day — the lamp lit before nine, the rope hung, the bell rung at eleven with the beam on the water — and the night is closed for good.
Some of what people ask you is a choice, not a question, and the village holds you to it — for a day. Tell Margaret that Tom should see the ledger and she walks it across the road at closing; hurry Ada through the story of that night and she says nothing more until the bell. Margaret will remember that. Ask Gilbert for the lamp oil one way and he slams the can down; ask the other and he fills it himself — and what he is doing when the light holds is different too. The reset forgets it; that is the loop.
The state is in two tiers, which is the whole design:
| Holds | On the bell | |
|---|---|---|
MetaState | loops, facts (and the loop each was learned in), unlocks, endings, the days since the loop broke | kept |
LoopState | the hour, where you are, what you carry, what happened today (its flags, every one named in flags.py) | thrown away |
An ending is a row in loop.py's ENDINGS table — an hour, a flag, a handler — and the clock consults the table each hour. The light fires at nine and the bell at eleven, so a player who has done both leaves by the light.
The day is seeded, so the sea gives up the same fish to the same casts in every loop. A player who changes nothing sees nothing change.
Numbered slots under data/ (or TIDEWATER_SAVE_DIR), one save.json each, validated against schemas/save.json on every load and save. A save that can't be read is listed as damaged, never overwritten, and copied aside if you open it anyway.
In the browser the same slots live in the browser's IndexedDB (tidewater-saves), and the Saves panel can download them to a file and load them back in another browser. At tidewater.play.danielstephenson.dev, a player signed in to arcade can also turn on cloud backup (cloudSaves in web/index.html, through tak's cloud.js, Stephenson-Software RFC 0016). It is off until they do, nothing is sent before then, and no other host loads it.
Played at tidewater.play.danielstephenson.dev while signed in to arcade, Tidewater reports achievements through tak.arcade (Stephenson-Software RFC 0014): a few of the important facts, every clue the village holds, a choice someone will remember, and each way out of the loop (one of them hidden). They are listed, with ids and descriptions, in src/tidewater/achievements.py and declared in the gateway's config/play/boards.yaml. Anywhere else — the console, signed out, another host — nothing is sent. Unlocks are never written to the save file; loading a save re-reports what it has already earned.
Tidewater reports one startup event and one save-loaded event (program name and version only) to trace.danielstephenson.dev, on by default, and prints a one-line notice the first time an install does so. TIDEWATER_USAGE_REPORTING_ENABLED=false, TRACE_USAGE_REPORTING=off or DO_NOT_TRACK=1 turns it off, the browser build never reports, and what is collected and why is written up in the trace client's README.
In the browser, each of the seven places has one picture above its menu, rendered offline with the Strata engine from a scene file in art/ and shipped as a PNG in pictures/ inside the browser bundle. The console is unchanged: it shows no pictures.
The setting and the art are a provisional first pass (a late-September day at 54.5° N, flat-coloured shapes, no people drawn), recorded as provisional in art/stills.json with the reasoning for each choice. All of it, the alt text included, is the author's to change. All the stills.
To re-render after editing a scene or the manifest, build Strata's strata-golden tool at the tag art/stills.json names (see the top of art/render.py), then:
uv run --with pillow python art/render.py --strata-golden /path/to/strata-golden
The script places the sun from each still's date, time and place, and refuses to render a still whose sun is not filled in. The PNGs are committed, and tests/test_stills.py holds them to the manifest's size budget.
pip install pytest pytest-cov -r requirements.txt
./test.sh
tests/test_game.py plays the whole solution through a scripted front-end; if a menu label moves or a gate breaks, that test says which.
This project is licensed under the Stephenson Software Non-Commercial License (Stephenson-NC).
© 2026 Daniel McCoy Stephenson. All rights reserved.
You may use, modify, and share this software for non-commercial purposes only.
Commercial use is prohibited without explicit written permission from the copyright holder.
Full license text: Stephenson-NC License (also in LICENSE)
SPDX Identifier: Stephenson-NC