A noir detective game that teaches the ISO-GQL graph query language. You are the detective: read the evidence, build the case graph by pinning suspects and drawing red-string cords between them (each cord is a graph INSERT), then write GQL at the records terminal to interrogate what you built and name the culprit.
Powered by frogql-wasm — an in-browser, in-RAM ISO-GQL engine (Rust → WebAssembly).
Each case runs model → query → accuse:
- Model. Click highlighted names/places inside the evidence to pin them as nodes (
INSERT (m:Person {...})). Drag a cord from one card to another to add an edge (INSERT (m)-[:KNOWS]->(v)). Every action shows the GQL it ran. - Query. Write
MATCH/WHERE/RETURNagainst the graph you assembled to close each lead. - Accuse. Pick the culprit from the lineup.
Cases are progressive: The Vanishing Violinist (nodes, MATCH/WHERE/AND) → The Broken Alibi (edges, directed traversal, comma multi-patterns, EXISTS/NOT EXISTS).
npm install
npm run dev # http://localhost:5173
npm run build # production build (bundles the ~300 kB gzip wasm, no plugin)
npm run preview- React + Vite + TypeScript, Tailwind as a layout utility under a hand-built noir theme (CSS vars, grain/scanline/vignette).
- CodeMirror 6 terminal with a custom GQL highlighter.
- framer-motion scene transitions; Web Audio synth SFX (no asset files).
- zustand + localStorage for progress; react-i18next + inline
{en,es}case content (fully bilingual EN/ES).
src/
engine/ frogql.ts — the ONLY module touching frogql-wasm (init + GqlConnection)
graph/ builder.ts — the assembled graph + INSERT codegen, re-opened per change
content/ types.ts (Case schema) + cases/<id>/index.ts ← add a case = add a folder
verify/ order-insensitive model + query + accusation checks
game/ scenes/ (boot · office · desk · accusation · caseClosed) + zustand store
components/desk/ (Evidence · Pinboard · Terminal · Printout · Leads · InsertLog · …)
Create src/content/cases/<id>/index.ts exporting a Case, then register it in src/content/registry.ts. No engine or UI code changes. A case bundles bilingual story, evidence cards (clickable entities + cord edges), ordered leads with declarative verification, and the suspect lineup.
frogql-wasm supports MATCH, label logic (& | !), directed/reverse/undirected edges, comma multi-patterns, WHERE (= != < <= > >=, AND/OR/NOT, IS NULL, IS INT), RETURN ... AS, DISTINCT, EXISTS {}/NOT EXISTS {}, OPTIONAL MATCH, variable-length paths, and DML (INSERT/SET/REMOVE/DELETE).
It does not support ORDER BY, GROUP BY, LIMIT, UNION, or aggregation — and silently ignores them rather than erroring. All puzzle verification is therefore order-insensitive, and the terminal warns when a query uses an unsupported clause.