A championship-level Gomoku & Renju engine running entirely in your browser.
Play, analyze, review and research. No server, no install, no account.
▶ Live demo: rapfiweb.pages.dev
English · 한국어
Rapfi is one of the strongest open-source Gomoku/Renju engines, a C++ engine with NNUE evaluation. RapfiWeb compiles it to WebAssembly and wraps it in a full analysis workbench. A desktop GUI, an Android app and a server farm, all inside one browser tab.
- Nothing leaves your device. Games, reviews, databases and statistics live in IndexedDB.
- Works anywhere static files can be hosted. No backend, no API keys, no telemetry.
- Fast. Multi-threaded SIMD search, ~20 MB of network weights cached after the first visit.
- Rapfi compiled with Emscripten into four variants:
{single, multi}-thread × {SIMD128, Relaxed SIMD}. The app probes the browser and boots the fastest one, falling back step by step. - Pthreads on
SharedArrayBufferwhen the page is cross-origin isolated (COOP/COEP), with a time-sliced single-threaded fallback everywhere else. - Weights are fetched once, SHA-256 verified, and served from Cache Storage afterwards.
- Engine patches are measured, not guessed: SPRT matches, mate suites and fixed-depth benches run in CI. The deployed build scores +100 Elo over a stock Rapfi WASM build at 1 thread, 500 ms/move (report).
- Drop the RenjuNet archive (
.rif, 43 MB, ~119,000 games) onto any screen. A worker streams the bytes, hashes every position with incremental 64-bit Zobrist keys and builds a 15 MB statistics pack. - 3.7 s on a real Android phone (Chrome), 4.6 s on a Celeron N4500, with 50–57 MB peak worker memory. Opening frequencies, rankings by player, color and rule, and game lists follow instantly.
- Verified position-for-position (116,191 positions) against the desktop toolchain's packs.
- Every position is read with the top two candidates (
YXNBEST 2); each move is graded on an 11-step scale (brilliant, best, … mistake, blunder, missed win) with per-side accuracy. - Batch queue with per-position checkpoints. Close the tab and it resumes where it stopped.
- Export to CSV, Markdown, or a self-contained HTML report with board, eval graph and worst moves.
- Also included: AND/OR proof search for forced wins, and a move-order explorer that counts every sequence reaching the current stones.
- Games autosave on every move, undo included. Reload and keep going.
- The engine's own position database (yixindb) is round-tripped between Emscripten's
in-memory FS and IndexedDB. Annotate, label, import and export
.dband CSV. - Official opening curriculum: maintainer-curated joseki you study read-only. Explore every branch with commentary, bad moves, traps and refutations, or switch to Learn and guess each move (hints, scoring, progress saved in the browser). Published as a static file, no server.
- Joseki editor: variation trees, Markdown notes with clickable coordinates, auto classification, and lookup by board position (symmetry-aware).
- Open and save
.psq,.savand.pos; share any position as a?p=h8i9j10deep link; import and export desktopsettings.txtbyte for byte.
- Full Korean and English UI (~900 strings) with auto-detection and a switch in Settings. A test scans the source so no string ships untranslated.
- Responsive from 360 px phones to desktops, in portrait and landscape, light and dark themes.
- Accessibility built in: keyboard play on every board (arrows + Enter), screen-reader announcements, dialogs that take focus and close on Esc, 24 px minimum touch targets, and WCAG 4.5:1 contrast in both themes, all enforced by a Playwright smoke test.
┌──────────────────────────── Browser tab (main thread) ─────────────────────────────┐
│ │
│ React 18 UI ── Zustand stores ── Canvas 2D board (2 layers) │
│ │ │ │
│ │ └── Dexie ──► IndexedDB games · reviews · queue · joseki │
│ │ dbfiles · packs │
│ │ EngineClient (typed messages, position-keyed replies) │
└────────┼───────────────────────────────────────────────────────────────────────────┘
│ postMessage
┌────────▼──────────────── Engine Web Worker ───────────────────┐ ┌─ Other workers ─┐
│ engine-worker.ts │ │ rif.worker │
│ ├─ Gomocup / Yixin text protocol (sendCommand ↔ lines) │ │ .rif → pack │
│ ├─ weights-cache: Cache Storage + SHA-256 → rapfi.data │ │ moveOrder.worker│
│ └─ Emscripten module (Rapfi C++ → WASM) │ │ sequence DAG │
│ ├─ MEMFS /rapfi.db ◄── synced ──► IndexedDB │ └─────────────────┘
│ └─ pthreads on SharedArrayBuffer (multi variants) │
└───────────────────────────────────────────────────────────────┘
- One owner per engine. Only the worker touches the WASM module. The UI sends typed commands and draws a reply only if its position key matches the current board, so stale analysis never flashes after undo.
- Exclusive modes. Play, analysis, review and proof share one engine through a lock, and each mode knows how to yield.
- Variant fallback. relaxed multi → multi → relaxed single → single, decided by capability probes and a warm-up handshake. Browsers without WASM SIMD get a clear notice.
| Layer | Tech |
|---|---|
| UI | React 18, React Router 6, Zustand 5, Canvas 2D, design tokens generated from TS |
| Engine | Rapfi (C++) → Emscripten → WASM SIMD128 / Relaxed SIMD, pthreads |
| Storage | IndexedDB via Dexie 4, Cache Storage for weights |
| Quality | TypeScript strict, Vitest (50+ test files), Playwright smoke tests, ESLint, Prettier |
| Delivery | GitHub Actions → Cloudflare Pages (COOP/COEP headers for threads) |
Requirements: Node.js ≥ 20.11 (CI uses 26) and pnpm.
git clone https://github.com/lowqen/RapfiWeb.git
cd RapfiWeb
pnpm install
# Engine weights (~20 MB) are a release asset, not part of git.
# The tag must match src/engine/engine-manifest.json.
gh release download engine-3c94c2a9-w8 --dir public/engine --pattern 'rapfi.data'
pnpm dev # http://localhost:5173Without rapfi.data the UI still runs; the engine indicator simply reports an error.
| Command | What it does |
|---|---|
pnpm dev |
Generate design tokens and start the Vite dev server |
pnpm build |
Tokens, type check, production build into dist/ |
pnpm preview |
Serve the production build locally |
pnpm test |
Unit tests (Vitest) |
pnpm check |
Type check, lint, test, format check and build: what CI checks (tsc, ESLint and Prettier keep caches, so repeat runs are fast) |
pnpm check:quick |
Before a commit: type check, lint, format, and only the tests affected by your changes (CI still runs everything) |
pnpm engine:build |
Rebuild the WASM engine from source (Linux x86_64, Emscripten) |
Deployment to Cloudflare Pages is automated by deploy.yml;
see docs/배포-cloudflare.md for the one-time setup.
WebAssembly SIMD and the CSP keyword 'wasm-unsafe-eval' are required: Chrome/Edge 97+, Firefox 102+,
Safari 16.4+. Multi-threading
additionally needs cross-origin isolation (public/_headers). On hosts without it, RapfiWeb falls
back to the single-threaded engine automatically.
This web build ships Renju weights only (Cloudflare's 25 MB per-file limit).
| Document | Contents |
|---|---|
docs/기능-안내.md |
Detailed feature guide, screen by screen (Korean) |
docs/최적화-마스터플랜.md |
Engine optimization log: every patch, its measurement and verdict |
docs/gomocalc-대국-분석.md |
Match analysis behind the +100 Elo figure |
public/engine/README.md |
Engine variants, build provenance, weight caching |
RapfiWeb is released under the GNU GPL v3.0 or later because the bundle includes the Rapfi engine. Code written for this repository may also be used under BSD 2-Clause. Bundled and ported components (Rapfi, rapfi-networks, Yixin-Board, RapfiDroid, npm dependencies) are listed in THIRD_PARTY_NOTICES.md, together with where to find the engine's corresponding source.
RenjuNet data is never bundled. Its license permits non-commercial, offline use only, so the
app processes your local copy inside your browser and nothing is uploaded. .gitignore and a CI
gate keep .rif-derived files out of the repository and every deployment.
- dhbloo/rapfi and rapfi-networks: the engine and its networks
- Yixin-Board by Kai Sun, and dhbloo's fork: the desktop GUI whose protocol and review logic this app follows
- RapfiDroid: the Android analysis board this web app was ported from
