Crimson Moon
A werewolf game you can play alone. Eight AI players hold the other seats. You still speak, vote, and risk being wrong. Upright cards keep the village. Reversed cards are the witches.
Tech Stack: React 19, Vite, Electron, Cursor. The complete source code is open and available on GitHub.
The Design Intent: The Missing Puzzle Piece
Social deduction games are inherently thrilling, but for those with social anxiety, they often carry an exhausting emotional cost. The pressure to perform, argue, or deceive in front of a crowd creates a high level of internal friction, keeping many from enjoying a genre they might otherwise love.
Crimson Moon was built on the philosophy that AI is not just a functional gimmick, but a crucial puzzle piece capable of supporting humans and filling emotional voids. By replacing a room full of people with a reactive AI cast, the game removes the real-world social drain while preserving the psychological depth. It empowers players to safely explore experiences they might normally shy away from—allowing them to step out of their comfort zones, take risks, be accused, and execute bold plays without the anxiety of a human audience.
1. A Full Table, Without a Crowd
Werewolf usually needs a room of people. Crimson Moon seats one human and eight AI players, and still plays a full deduction game. You speak, you vote, and you can be wrong. Most importantly, you do not have to coordinate schedules to play a psychological thriller.
Nine seats exist at the table. The human is one, while the other eight are drawn from a pool of fifteen AI players. Two seats are dealt reversed witches. Card numbers are strictly unique—meaning two living players cannot truthfully claim the same Major Arcana. Civilians win when every reversed witch is exiled. Witches win when the number of living civilians equals or falls below the living witches—at which point a voting majority is mathematically impossible—or when the village is wiped out. The rules host is a robust engine in src/engine/game.ts. The client is built in React 19 and Vite, fully localized in Chinese and English, playable in the browser or via a Windows Electron build.
2. The Symbolic Engine (An AI Brain Without the LLM)
In Crimson Moon, the language model is strictly a flavor engine, not a decision maker. The characters strictly decouple the "mind" from the "mouth"—they form suspicions, hold grudges, and make strategic choices through a hard-coded "brain." The game plays perfectly, maintaining its full tactical depth and rule integrity, even if you turn the language model entirely off.
The core decision-making is a heavily weighted scoring engine.
Trust Ledger: Each seat maintains a strict trust[a][b] matrix (−50 to 50). Actions trigger hardcoded mathematical shifts. For example, if someone publicly claims you are Reversed but you know you are Upright, your trust in them plummets. If they publicly clear you as Upright, your trust surges as you naturally side with them.
Speech Planner (planSpeech): The engine evaluates logic before drawing from a weighted bag of intents (e.g., self-defense is 7–7.5, chatter is 1.4). Before the draw, three absolute rules apply: a true Priestess must report her morning investigation; a designated Witch can preemptively claim or counter-claim the Priestess; and if someone claims your exact card, you must counter-claim.
Evidence & Suspicion: Accusations require hard evidence. Without it, high-weight intents degrade into mere questions. Accusing the human player requires breaching a slightly wider threshold (+4 leeway). Verified Uprights, your verifying Priestess, and fellow witches never enter the suspicion pool.
Targeting (chooseAbilityTargets): The Upright Empress resurrects her most trusted dead player, but strictly abandons the action if trust is ≤ 0. The Witch targets based on a calculated threat score: current suspicion, plus the target's claimed role weight (e.g., Priestess 30, World 26), plus their speech frequency multiplied by 1.5.
3. LLM as a Flavor Engine
The AI players talk like real people at a table. You can answer with a prepared line, write your own, or name someone to pull them into the conversation. When the language model is engaged, it doesn't decide what the character does—it only determines how they express the engine's decision.
Strict Boundary Calls: The LLM (src/engine/llm.ts) only rewrites a finished plan. It never chooses targets, votes, or abilities.
Lean Context Window: The prompt is surgically precise. It only receives the current task, character name, vocal quirks, the day, phase, audience, survivor list, claimed cards, investigation results, the last 6 audible lines, and the engine's pre-calculated reasoning. Private logs are explicitly excluded. (Temperature 0.95, max 280 tokens).
Validation & Fallback: The output is aggressively validated. If the LLM drops the intended target, fails to report "Upright" or "Reversed," hallucinates the Priestess role onto the investigated target, or times out (28s local qwen2.5, 14s cloud gpt-4o), the generation is immediately discarded. The UI safely falls back to the engine's raw text.
4. Phase Isolation & Information Architecture
A day in Crimson Moon has a strict order. The Tea Party is a small, private gathering where secrets are shared, while the Trial is the only place for public accusations. This structure creates dramatic irony: you might hear a lie at the public Trial that contradicts what you heard in the private Tea Party.
The engine strictly encapsulates game state into six phases: Dawn, Morning, Tea Party, Trial, Nightfall, and Sabbath. Information isolation is achieved cleanly through input scoping rather than complex obfuscation. The AI planner filters inputs via an observer list (knownActs).
5. Pacing & The Two Clocks
Games should respect the player's cognitive load. By default, Crimson Moon does not force a timer; you advance the exchange by clicking, ensuring that slow readers are not punished by walls of generated text. However, when you want the pressure of a tighter, high-stakes table, you can enable a live countdown clock.
The game engine supports dual temporal states. When settings.realtime is off, the phase clock halts and awaits manual state progression. When toggled on, the engine runs a live tick-based countdown, governed by a global speed control multiplier in Options. State serialization ensures that an in-progress match—regardless of which clock mode is active—can be safely saved to localStorage and resumed later without desyncing the game loop.
6. Cards With Two Faces
Every Major Arcana has two abilities. Upright, she protects the village. Reversed, she is a witch. A match deals a new set of AI players and a new set of powers each time. The Gothic atmosphere stems directly from the mechanics: your fate is tied to a physical card that can flip, rather than an invisible "wolf" label arbitrarily assigned in a lobby.
Card uniqueness is a hard rule. The abilities are driven purely by numerical values in game.ts, separate from flavor text in src/data/arcana.ts. Headless scripts can simulate matches or print transcripts without ever opening the client, proving the complete decoupling of logic and UI.
7. Asset Pipeline: Look, Motion, and Score
The table feels like a physical place. Cards are richly illustrated, and ordinary play uses an instrumental track. The full song, with lyrics, arrives at the climax of the game. Public card moments are accompanied by atmospheric video clips that play exactly when an ability resolves. Every asset was crafted through a modern AI-assisted pipeline, meticulously directed to maintain a cohesive, Gothic aesthetic.
Art Direction: Character portraits, Tarot card illustrations, and game backgrounds were initially generated using Midjourney, then heavily processed and refined in Photoshop to ensure stylistic consistency and correct UI framing.
Motion Graphics: The animated Tarot card reveals in assets/tarot/vfx were created by animating the static base images using Runway's video generation (Seedance).
Audio Engineering: The score was generated using Suno, driven by custom lyrics I wrote, and the full original soundtrack has been officially released across major music streaming platforms. Master, music, and effects are routed to separate volume sliders in the client, allowing the table to be quiet without going mute.
Vite Range Requests & File Locks: During development, the video clips are served by Vite using 206 Partial Content Range Requests so long videos start immediately. To prevent the dev server from crashing due to large file locks, these directories are explicitly excluded from file watching. In production, the Electron build uses loadFile to read videos directly from the local filesystem, bypassing the middleware.
Lean Playback: The client avoids memory-heavy video pools. There is only one <video> element on the table (preload="auto"). When a new card appears, the video timeline resets and waits for canplay. Once finished, the video element pauses and the UI swaps to a static canvas render, freeing up rendering resources.
Performance: Judgment Dusk
This page features the game's original theme, "Judgment Dusk." Hime takes the stage beside the lyrics, her mouth seamlessly synced to the vocals as she performs the track.
「何も知らない愚者(0)が、目を覚ます。これが、絶望のゲームの始まり……」
正午の異象が、空を歪めていく魔術師(I)の嘘が 真実を塗り替え女教皇(II)の書物は 沈黙に沈む女帝(III)の愛も、皇帝(IV)の法も教皇(V)の祈りさえ、ここでは無力……
招かれたのは、疑心暗鬼の午後の茶会(ティーパーティー)恋人(VI)たちは裏切り、戦車(VII)は血を轢(ひ)いて暴走する力(VIII)なく震える手に、隠者(IX)のランプが落ちて狂い回る運命の輪(X)、正義(XI)の天秤が壊れた……
ああ、黄昏の審判会(オラクル)が幕を開ける!吊るされた男(XII)の骸(むくろ)に、死神(XIII)の鎌が振り下ろされ節制(XIV)を失った、悪魔(XV)たちのデスゲーム!誰かを魔女に仕立てなきゃ、自分が殺される希望なんて、処刑台の上の幻影(まぼろし)!
逃げて……!真夜中の魔女狩り(ウィッチハント)が来る!雷鳴に崩れ落ちる塔(XVI)!祈りを捧げた希望の星(XVII)も、粉々に砕け散って……!
狂い咲け、猩紅の月(XVIII)に照らされて黒い太陽(XIX)が、本当の化け物を暴き出す終わりの審判(XX)が告げる、生き残った者の正体……私こそが、あなた達を殺した魔女。
夜明けが訪れた、からっぽの世界(XXI)。誰の足音もしない。……これで、よかったんだよね?
(Listen to the complete original soundtrack on Spotify, Youtube Music, and major streaming platforms.)
