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,即可验证所有权并自行管理介绍。
一分钟完成验证正在加载完整 README。 以下为已保存的摘录。
(The repository is still called kanji-battle; only the name in the game changed.) Real-time kanji battles in the browser. Five modes: Production: npm run build && npm start (PORT supported). On Render the build command must include npm install (e.g. npm install && npm run build) so the postgres package gets installed. Two accounts are created on first start: ⚠️ This repository is public, so these default passwords are public too. On the live site set ADMIN_PASSWORD (and optionally USER_PASSWORD) — the server switches the account to it on start.查看完整 README ↗
(The repository is still called kanji-battle; only the name in the game changed.)
Real-time kanji battles in the browser. Five modes:
| Mode | How you cast |
|---|---|
| 1v1 Kanji Reading | Study 10 words for 60 s, then see the kanji and type its reading (IME kana or romaji). |
| 1v1 Kanji Writing | Look at the kanji (up to 3.5 s) and press CAST! (or Enter): it vanishes and only then the pad / Japanese keyboard appear, with the reading + meaning. Pasting into the answer box is blocked and the kanji can't be selected. Type the kanji with a Japanese IME or draw it on the pad. Only kanji count (kana only at the かな level). |
| Boss Elimination | 1–4 players vs the Black Dragon (its HP grows with the party). Correct answers hit it; a mistake gets you clawed; fire breath every 30 s hits everyone who isn't on fire (5+ combo = immune). |
| 1v1 Rapid | Both players get the same kanji. First correct reading (kana or romaji) deals the damage. Levels are shared: in a room one set for both, in the online queue only players with a level in common are matched. |
| Deck Duel | Pick a hero, draft 10 kanji cards from a pool made of both players' All spells, then spend mana to cast them by writing the kanji. 800 HP each. |
npm install
npm run dev # http://localhost:3000
npm test # unit tests
Production: npm run build && npm start (PORT supported). On Render the build command must include
npm install (e.g. npm install && npm run build) so the postgres package gets installed.
public/manifest.webmanifest + the service worker public/sw.js
(generated from src/client/sw.js by scripts/build-client.mjs, with the list of files to keep).
Phones and PCs can install the site as an app (menu → Get the app); it opens without internet.desktop/ (Electron). It opens the live site (always the newest version), so the
service worker gives it offline mode too; offline.html is shown only if the very first start has no
internet. Built into KanjiWizards-Setup.exe by electron-builder.mobile/android/ (a small WebView app, no Google Play needed). Adds Android's
text-to-speech for spoken words, the file picker for profile pictures and invite links. Signed with the
beta key mobile/android/kanjiwizards-beta.jks, so new APKs install over old ones..github/workflows/apps.yml builds both on every pull request (download them from the
run's Artifacts) and, on main, publishes them as the GitHub release v<version>; the game links to
releases/latest/download/KanjiWizards-Setup.exe and …/KanjiWizards.apk.src/client/offline/. The app keeps a snapshot of your account
(GET /api/sync/snapshot) and, without internet, answers the study calls itself with the server's own
code (src/server/router.ts, StudyService, DailyService bundled for the browser). Changes go to an
outbox that is replayed in order once the server answers again, each call stamped with X-KW-At (the
moment it really happened, at most a week back) — so cards, streaks and daily-challenge times come out
as if you'd been online. Online queue, private games, friends and the admin pages need the server.assets/icon.svg → node scripts/build-icons.mjs (needs sharp).Two accounts are created on first start:
| Login | Password | Role |
|---|---|---|
user | user123 | user |
admin | adminn123 | admin — sees 👥 Users in the menu and can ban/unban players |
⚠️ This repository is public, so these default passwords are public too. On the live site set
ADMIN_PASSWORD(and optionallyUSER_PASSWORD) — the server switches the account to it on start.
Passwords are hashed with scrypt; logins are signed tokens (30 days).
| Variable | What it does |
|---|---|
DATABASE_URL | Needed on Render to keep accounts, XP and study cards (Neon, see below). |
AUTH_SECRET | Set this. Any long random string. Without it, everyone is logged out whenever the server restarts. |
ADMIN_PASSWORD | Set this. Replaces the public default admin password. |
USER_PASSWORD | Optional: password for the demo user account. |
GEMINI_API_KEY | Optional: turns on the reading passage's AI stories (one short story written with the player's words) using Google Gemini — it has a free tier (get a key at aistudio.google.com). Default model gemini-flash-lite-latest. |
ANTHROPIC_API_KEY | Optional, instead of Gemini: the stories come from Claude (default claude-haiku-5-5). Pay-as-you-go from console.anthropic.com. |
OPENAI_API_KEY (+ OPENAI_BASE_URL) | Optional, instead: any OpenAI-compatible API — OpenAI (default), xAI Grok (https://api.x.ai/v1), Groq (https://api.groq.com/openai/v1), OpenRouter (https://openrouter.ai/api/v1). Set STORY_MODEL to that service's model name. |
STORY_MODEL | Optional: the model for the stories. STORY_DAILY_MAX: stories a day for everyone together (default 400; each player up to 40). Without any key the passage uses the words' example sentences. |
USERS_FILE | Local only: where the file store writes when there is no database (default .data/users.json). |
Render's free plan wipes the server's disk on every deploy and restart, so without a database all registrations disappear. Neon has a free Postgres plan:
postgresql://neondb_owner:…@ep-xxxx.eu-central-1.aws.neon.tech/neondb?sslmode=require.DATABASE_URL with that string. Also add
AUTH_SECRET (any long random text) and ADMIN_PASSWORD. Save — Render redeploys.Accounts: postgres. The server creates its tables (kw_users,
kw_cards) by itself; no SQL needed.The connection string is a password: keep it only in Render's environment, never in the code (the browser only talks to this server, never to the database).
Click your name (top left) for your stats: level and XP, wins, losses, win rate and spells
learned. You can upload a profile picture there; it's cropped to a square, shrunk to 128×128 in your browser
and shown next to your name in lobbies and games (GET /api/avatar/:id, max 60 KB, PNG/JPEG/WebP only).
Five painted scenes, each in day, sunset and night (Customize → Cycle: day → sunset → night, 3 minutes each, fading out and back in; it never changes during a game): a forest with comets and red eyes in the dark, a misty swamp with will-o'-wisps, plains with villages, a windmill and wandering goblins, a castle with the Black Dragon circling above and soldiers clashing at the sides, and the World Tree. Each has its own sounds in the menus (owls, frogs, goblin chatter, sword clashes, chimes) at the music volume. Admins have every background unlocked.
3D arenas (0.9.9). In the 3D arena each background is its own place, ported from the Blender scripts
(src/client/arenas/: a shared kit plus forest, swamp, plains, castle, worldtree): a painted sky dome
(colour by elevation, sun / moon glow, far ridges, a Milky Way at night), a slowly turning cloud dome, crisp
star points and twinkling sparkle stars, then each scene's own parts — swaying trees with blinking red eyes,
water with ripples and mist, 3D mountains with snow and twin suns, two castles with waving flags and clashing
warrior silhouettes, the giant World Tree above a sea of clouds. Shape keys became morph targets; keyframed
animation runs off a 30 fps clock. Sunset and night use the scripts' themes; day is derived from sunset. Lite
builds fewer trees, tufts and lights and a smaller sky texture.
3D staffs (0.9.9). The five staffs are ports of the Blender staff scripts (src/client/staffs3d.ts, helpers in
src/client/geokit.ts) with their animations: Verdant's leaves sway, Ember drips lava, Tide's crescent waves melt
into a whirling stream every few seconds, Storm's shock rings and bolts crackle, Void pulls stars into its core.
Mouse look (0.9.9). In Full 3D the mouse turns your head (not over the pad, buttons or cards, and not while
drawing); a look message (≤ 8/s, relayed by the room) turns your character's head on the other players'
screens. Lite keeps the old parallax.
The Users page (admins only) shows who is online right now and what they are doing (in the menus, in the queue, in a lobby, playing a mode), and for offline players when they were last seen (since the server started) or their last login day. Online players are listed first; the page refreshes itself every 15 s.
Inbox collects what players send with Feedback (main menu): bug reports and suggestions, each with the player's name, the game version and the device type — plus errors caught automatically (a crash in a browser, or an unexpected server error; the same error is stored once a minute at most). Mark items done or delete them; the Admin button shows how many are open. Players can send 5 messages per 10 minutes.
Stats shows players online, active today, accounts, how many came back on day 1 and day 7, a 14-day chart of
active and new players, the tutorial funnel (signed up → started → kana question → first battle → finished or
skipped) and games started/finished per mode this week (src/server/study/InsightsService.ts).
Private game (main menu, under the queue) opens Create room and Join by code for playing with friends.
The Online queue button shows how many players are online.
Online queue on the main menu: tick the modes you'd play (Reading, Writing, Rapid, Boss) and the levels you
bring, then Find match — or queue for Deck Duel on its own. A timer counts while it searches; as soon
as another player wants a mode you ticked, everyone accepts, a room is made and the game starts right away
(same as joining by code and pressing Start). Boss Elimination waits for a full party of 4 (always 4 vs the
dragon in the queue — never 1, 2 or 3); the search screen shows how many are queued for it. First come, first
served (src/server/Matchmaker.ts).
📜 Match history on the main menu lists your last 15 games (older ones are deleted): the character you played, the mode's logo, VICTORY /
DEFEAT and the date. Open one to see its results screen again. Click any player's name (lobby, battle, results,
history) to see their profile: level, wins, losses, win rate and spells learned. Stored in kw_matches (Postgres).
Opening the game on consecutive days builds a 🔥 login streak (by your own calendar day); your current and best streak have their own boxes in your profile and on your profile card for others. Your best streak unlocks magic staffs (so a broken streak never takes one away): Verdant (everyone), Ember 3 days, Tide 5, Storm 8, Void 10.
New accounts get a short first-run tutorial (a "try it" box: 山 → yama), then a guided first battle against a beginner AI with tips on each screen — a quick one (30 s of study, a 2-minute fight) with かな and N5 words. Modes unlock with levels: Kanji Reading and Rapid from the start, Kanji Writing and Boss at level 1, Deck Duel at level 2 — for creating rooms and the online queue (checked on the server). Joining a friend's room by code or invite always works. Accounts from before 0.9.6 skip the tutorial.
Hiragana first: the tutorial asks whether you know hiragana. Beginners get only かな — for studying, rooms
and the queue (checked on the server), and no Deck Duel (it is all kanji) — until they tick I've mastered hiragana in Settings; then N5–N1 open.
Stored as a flag in the account's unlocks.
The same 10 words for everyone each day (UTC), two each from N5 up to N1. Type each reading (kana or romaji)
within 15 s; answers are checked one at a time on the server and the clock runs there too (leaving a word
open counts it as missed). One try a day, ranked by correct answers then total time; 30 XP per correct word
(+100 for 10/10). After playing you see the words with their readings, each marked ✓ or ✗, and the missed ones go to
Struggling spells (src/server/study/DailyService.ts; per-word marks in kw_daily.marks).
Progress (main menu): this week vs last week (days active, cards passed, games, wins, XP), an activity
calendar (one month, Monday first, ISO week numbers, ‹ › back up to six months), and a mastery grid for every word of each level (not yet in your spells / new / learning /
learned / mastered = review interval of 3+ weeks). Activity is logged per player per local day (kw_activity).
Every month has the same three goals — play on 12 days, win 10 games, pass 150 flashcards — and a seasonal reward: even months a flame colour (Sakura, Rain, Sun, Thunder, Harvest, Starlight), odd months a staff (Frost, Blossom, Jade, Abyss, Moon, Maple — recoloured Verdant, Ember and Tide staffs, in 3D and pixel art; Storm and Void stay exclusive to long login streaks). Rewards are kept forever once earned. Customize lists every seasonal staff and flame — locked ones show which month's goals unlock them. The goals panel shows the reward itself (the pixel staff, or a little wizard in the flames).
Friends rail (right edge of the menus, like Valorant's): pictures only; hover it and it opens with names, levels and what everyone is doing (online / offline groups). Click a friend for their profile. Add friend and Requests at the bottom open a small window. Hidden in games. On phones a round Friends button (bottom-right, with the online count) slides the same list in from the right. The menu no longer has a Friends button.
Friends (main menu): add players by name, accept or decline requests, see who is online and what they are
doing. In a room's lobby your online friends appear with an Invite button; they get a card with Join /
Decline (and a notification if the tab is in the background). Stored in kw_friends. Friends already in your room
aren't listed, friends in a game show In a game (no invites mid-game), and the same friend can be invited
again after 10 s (checked on the server too). Accepting an invite while you're in another room's lobby or results
leaves that room and joins the new one.
Study spells: after flipping a card, Stroke order (or S) draws each stroke of the kanji in order, numbered.
After a wrong or missed write in Kanji Writing or Deck Duel the correct strokes play right away, and on the
results screen every word opens its stroke order. The strokes are the handwriting recogniser's reference data
(/api/strokes).
Deck Duel plays a chime when your turn starts; "Your turn", "Match found" and invites blink the tab title and show a system notification while the game is in the background (the browser asks for permission once, when you start queueing or create a Deck Duel).
The 3D arena now has Full / Lite / Off (settings panel). Lite (default on phones and small screens) uses less
grass and particles, no anti-aliasing and a lower resolution. On a phone held upright the 3D arena fills the
screen too: cropped at the sides, your hand and staff moved in so they stay in view, and the profile and settings
buttons hidden while you play (they return after the game). The page shrinks with the keyboard
(interactive-widget=resizes-content), and the answer box stays enabled between words (locked, not disabled), so the
keyboard stays up instead of closing and reopening on every word. Sideways it fills the screen as on a computer. If 3D runs slowly the arena drops to
Lite by itself (then to a lower resolution) and says so. On phones the mode's name sits under the top bar. Upright phones use
the 2D layout with a bigger writing pad, a compact top bar and stacked pages.
Three sections, each with a live preview and an Equip button:
src/client/staffpreview.ts, models in src/client/staffs3d.ts). Unlocked by login streak.At 5 correct casts in a row you are wrapped in flames — and in Deck Duel while your Omnipotence (hero power) is active. The colour is yours to pick in Customize: light blue (default) or purple (from level 5; admins have everything), and everyone sees it. If more than 100 Struggling spells are waiting (new, learning or due), the game modes lock until you study them down to 100 (checked on the server too; admins are never locked).
The host can add AI players in the lobby (🤖 Add AI) and pick how much they know: Beginner (かな + N5 words,
slow, wrong about half the time — the tutorial's opponent) or N5 to N1. Each AI holds a random staff and flame colour. They fill an
opponent's seat in the duels, Rapid and Deck Duel, or join your party against the dragon (up to 3 AI). The
chance that an AI gets a word right (src/server/Bot.ts):
| AI knows → / word level ↓ | N5 | N4 | N3 | N2 | N1 |
|---|---|---|---|---|---|
| AI Beginner | 55 % | 30 % | 15 % | 5 % | 2 % |
| AI N5 | 90 % | 50 % | 20 % | 10 % | 5 % |
| AI N4 | 92 % | 75 % | 35 % | 15 % | 10 % |
| AI N3 | 95 % | 85 % | 70 % | 30 % | 18 % |
| AI N2 | 99 % | 90 % | 80 % | 60 % | 30 % |
| AI N1 | 100 % | 95 % | 87 % | 70 % | 60 % |
(Hiragana words count as N5.) AI players answer after a human-like pause, always vote for a rematch and are always Ready. Matches with an AI give half XP; a room with only AI left closes.
src/shared/srs.ts).Battles and Deck Duels are shown first person in a 3D arena (Three.js, src/client/arena3d.ts, loaded on demand
as public/arena3d.js): your hand holding your staff in the foreground, the opponent (or the dragon and your party)
across a stone arena with torches, your chosen background and time of day far behind. Move the
mouse to look around a little — the camera turns towards the mouse. Spells are kanji that fly across with a trail; hits flash and shake the
camera. You play in first person: your hand (sleeve and skin after your character) and your staff skin — while you
write or type the staff tilts back, charges and traces a figure-eight (∞) in the air; when you cast it thrusts forward
and its gem flies off with the spell. The gem is gone until you start writing or typing again, when a new one builds
up — a miss or a wrong answer shatters it the same way (also in 2D). A 5× combo (or your Omnipotence in Deck
Duel) sets your staff on fire and sends mana fire in your flame colour flowing along the bottom of the screen (a
shader, a soft animated band over the lower quarter of the screen), with a big combo counter. The flames end with the
game. Ambient motion (fire, sway, the gem) runs even with the system's "reduce motion" on; only camera shake follows it. Opponents and teammates cast the same way: a short wind-up, then the staff thrust forward at the target. In Boss fights, downed
teammates lie on the ground (and you lose your staff). The others
are low-poly 3D characters holding staffs (wizard, witch, goblin, knight, apprentice, adventurer — their level's
avatar, or their Deck Duel hero). Deck Duel cards hit with their own effect: Bolt → lightning, Frost → the target turns see-through
icy blue for a second, Inferno → it burns. The dragon stays a pixel sprite. Only the look changed — all rules are the
same. Switch it off in the sound/settings panel (🏟 3D arena); phones, small windows and browsers without WebGL
use the classic 2D view.
UI size: the interface is drawn at 80% by default on computers (so Deck Duel's HP and mana stay on screen even
with the Japanese keyboard's candidate list open); change it in the settings panel (70–100%, saved per browser;
src/client/zoom.ts). Phones and small windows stay at 100%.
Settings → Language: English or Русский. Dates and times always follow the chosen language (not the browser's).
Russian is a translation layer over the English interface (src/client/i18n.ts: exact strings plus patterns for texts
with numbers and names); Japanese text stays as it is. Word meanings are shown in Russian too, from
public/meanings-ru.json (all 6,316 English meanings, translated for this game; loaded only in Russian).
Twenty badges on the Progress page, beside the activity calendar: hexagons with their own icon and colour, one to
three dots for how hard they are; locked ones are dimmed, and hovering one says how to earn it. First Victory,
Veteran (25 wins), Champion (100), On Fire / Inferno / Unstoppable (5× / 10× / 20× combo), Flawless (a win at 100%
with 10+ answers), Dragon Slayer, Card Sage, Calligrapher, Quickdraw (a win in Boss / Deck Duel / Writing / Rapid),
Scholar (100 spells learned), Archmage (1,000), Kana Graduate, Devoted (7-day streak), Eternal Flame (30), Perfect
Day (10/10 daily), Fellowship (a friend), Seasonal (a monthly reward), Grand Wizard (level 10). They are checked on the
server after every match and whenever the profile loads, and stored as ach:<id> in the account's unlocks
(src/shared/achievements.ts). Earning one plays an "Achievement unlocked!" animation — combo badges the moment the
combo happens in the battle.
In a private game where you are the only human (against AI players, or a solo Boss run), ❚❚ Pause (under the
clock) stops the game: the server's game clock stands still (PausableClock, src/server/clock.ts — timers, AI moves,
dragon breath, Deck Duel turns), answers are ignored, and Resume sends everyone the clocks again. Not available when
another human is in the room.
In Reading, Rapid and Boss on a touch screen, a compact keyboard (letters, -, n', ⌫, Skip, Cast) replaces the phone's own: about a third of the height, it never pops in and out, and it shows the typed romaji as kana above the keys. Settings → Battle keyboard switches back to the phone keyboard. The 3D arena then fills only the space above the keyboard, so your staff, combo counter and flames stay in view. Writing modes keep the phone keyboard (for the Japanese IME), with the pre-written kana shown big beside the typing box (お□ → お [ ], □ず → [ ] ず). On phones the party's HP in Boss fights is two compact columns. The 3D canvas is sized from the zoom-corrected viewport, so it covers the screen at every UI size. Opponents (and your party) trace the ∞ with their staffs while they work on a word.
Since 0.9.8 the levels N5 → N1 are re-sorted by how complex a word looks — the total stroke count of its kanji — so
an "N5" word is never harder to write than an "N1" one (study sets, battles and Deck Duel). Each level keeps its size;
a word's JLPT list is kept as jlpt (rebinByStrokes in src/server/vocab.ts). かな is unchanged.
Since 0.9.8.5 the JLPT list counts a little too: the sort score is strokes + 3 × JLPT step (N5 = 0 … N1 = 4), so
stroke count still leads but an N1 word rarely lands in N5.
In Kanji Writing and Deck Duel you write only the kanji: the kana of the word are filled in — the pad shows them printed
in their own shaded cells between and after the empty ones (必ず → [ ][ず], 売り場 → [ ][り][ ]). Writing the whole word still counts. Words of 2+ kanji forgive one shaky character (4+: two) if it has
about the right number of strokes and shape; with a Japanese keyboard, the kanji alone count, and in words of 3+
kanji one wrongly picked kanji is forgiven (writeTargets / isCorrectWriting in src/shared/kana.ts).
Typed readings accept one missing or swapped long vowel (おう / うう / おお: kyoto for きょうと, benkyo for べんきょう)
and a missing する at the end (seikatsu for せいかつする) — isCorrectReading in src/shared/kana.ts.
Heroes (their ultimate is Omnipotence, button bottom-left: costs 100 mana, then rests for 4 of your turns — Goblin 5; effects last 2 turns): Goblin casts 2 cards in a row (the second costs 1.5× mana), Knight −45 % damage taken and +50 % healing, Witch sees your hand's spells and readings and her attacks hit 35 % harder (the card she is writing still hides its kanji); Wizard is passive (2 random cards when out of cards, or +30 mana when out of mana, once each).
The lobby has no level picker (it shows an illustrated guide instead): both players press Ready and the duel starts. Hero pick and the first draft can still go ← Back to lobby. In battle there's a chat, and while your opponent plays you can read the list of kanji in your hand (sorted, so you can't tell which card is which). A card's mana is paid when you play it, even if you then write it wrong.
Draft pool (0.9.9.4.4): 4 words of each level N5–N1 — 2 from each player's All spells (a random word of that level when a player has too few, and for the AI). Draft: 20 cards (4 per colour), coin flip, picks of 2 (one 20 s clock for both) until each player has 10. Colours, ranked by word difficulty (JLPT level, stroke count and word length):
| Colour | Effect | Mana |
|---|---|---|
| Red | 340 damage | 45 |
| Green | heal 140 | 30 |
| Yellow | +60 mana | 0 |
| Blue | 150 damage | 20 |
| Light blue | 90 damage | 10 |
Mana starts at 200/200, +10 each turn. You get 15 s to choose a card. Then one minute to cast it: the card flips and
shows only its reading and meaning (15 s, or press Ready), then the kanji; press CAST! and the kanji
disappears while you write it from memory. Right or wrong, the card then turns over to show the correct
kanji with its reading and meaning for 2 s (nothing can be played meanwhile). A successful spell gives back half its mana. Writing is judged extra forgivingly here: besides stroke matching, the overall
shape is compared with all ~2,300 characters (src/server/handwriting/shape.ts), and one shaky character in a word is forgiven. Wrong or too slow → the card rips. Out of cards → a new draft round
(Round 2, 3 …: 20 fresh cards, each player picks 10 more; HP, mana, powers and unplayed cards stay, and the
match clock pauses). Having cards but no mana for any of them skips your turn (−100 HP, like any skipped turn); mana keeps coming back. After 8 minutes, overtime: the cards are gone and the duel becomes a
1v1 Rapid duel with the HP you have left — random kanji words (N5–N1, only the kanji shows), the first to type the reading
(hiragana or romaji, wrong guesses may retry) hits: N5 60 · N4 80 · N3 100 · N2 130 · N1 170 damage. 12 s per word;
after 3 minutes of overtime the higher HP wins. Letting the choose clock run out without playing a card costs 100 HP.
scripts/build-vocab.ts from
open-anki-jlpt-decks (MIT), which is based on
Jonathan Waller's JLPT lists (tanos.co.uk, CC BY). Licence in data/sources/.DEFAULT_CONFIG (src/server/Game.ts).src/server/Balance.ts).src/server/handwriting/KANJICANVAS-LICENSE.txt).
It's forgiving: a character counts if it's in the top 10 guesses or close to the best guess
(JUDGE in src/server/handwriting/judge.ts).src/server/handwriting/segment.ts).Uncaught server errors are logged to the admin Inbox instead of going unnoticed. On a restart (deploy) the server tells everyone "The game is updating — you'll be reconnected in a moment." before closing. Static files are sent gzipped.
See About in the main menu and CREDITS.md: word data from open-anki-jlpt-decks (MIT) / Jonathan Waller's JLPT lists (CC BY) / JMdict (EDRDG, CC BY-SA 4.0), handwriting recognition from KanjiCanvas (MIT) with KanjiVG data (CC BY-SA 3.0), kanji meanings and readings from KANJIDIC2 (EDRDG, CC BY-SA 4.0) via kanji-data (MIT), three.js (MIT). The About box also has a short privacy note.
src/shared protocol · vocab helpers · progress (XP, backgrounds, achievements) · srs · deck rulessrc/server Game (reading, writing, boss) · RapidGame · DeckGame · Balance · Room · RoomManager ·
Session · http (REST API) · db/ (memory, file and Postgres stores) · auth/ · study/ · handwriting/src/client net · api · ui · deckui · study · pad · wizard (pixel sprites) · backgrounds · audio · mainscripts/ vocab and handwriting data builders · test/ node:test suites