Call your shot. Sink the rack.
Classic 8/9-ball-style pool with real Box2D physics: drag back from the cue ball to aim and set power, then release to strike. Sink all 15 numbered balls in as few shots as possible — scratch the cue ball and it costs a stroke, but the table keeps going.
Made by Frank Force. Released under the MIT license — original source.
Changes for this platform: Patched this folder's own vendored littlejs.release.js: glInit()'s `glCanvas.getContext('webgl2', {antialias:glAntialias})` now also passes `preserveDrawingBuffer:true`. Without it, the WebGL canvas's drawing buffer reads back blank/transparent to any JS that inspects it outside the exact task that rendered the frame (the default back-buffer is cleared right after compositing) — this game's table/rails/balls/cue stick are all drawn via LittleJS's WebGL batch renderer onto glCanvas (the FIRST <canvas> in document order, ahead of the 2D mainCanvas used only for HUD text), so this platform's own headless correctness check (scripts/check-seed-games.mjs, which calls ctx.drawImage(document.querySelector('canvas'), ...) to verify the canvas isn't blank and changes on input) was reading a reliably-blank buffer and failing even though the game renders correctly on screen (confirmed via full-page screenshots both before and after this fix — the visual output is identical; this only fixes programmatic readback). Rewrote the 5 engine <script src> paths (engineLoader.js, gameFx.js, textureGenerator.js, box2d.wasm.js, menus.js) from '../templates/' and '../dist/' to './' since this self-contained game folder vendors its own copy of the engine/templates instead of sharing a repo-root dist/ or templates/ tree; this also carries box2d.wasm.wasm and littlejs.release.js into the folder. Used the already-patched templates/engineLoader.ARCADE.js (renamed to engineLoader.js) instead of upstream's engineLoader.js: it loads the engine from './' instead of '../dist/', wraps its littlejsBuild() console-debug helper's localStorage calls in try/catch, and redefines readSaveData/writeSaveData to route through window.ArcadeSDK.storage when present (so saveDataInit('pool')'s bestShots record persists via the host instead of the throwing sandboxed localStorage, falling back to upstream's own already-guarded localStorage behavior when run standalone). Added <script src="./arcade-bridge.js"></script> after menus.js and before the game's own inline <script> (new file, not patched into the original: calls SDK.ready() the first time menus.js's addPlayingListener fires (synchronously, with the title's initial isPlaying()===false), and SDK.onPause/onResume to route host pause into showMenu('pause')/installAutoPause()). Added three one-line call-outs inside index.html's own game logic, guarded with '&&' so they no-op standalone, because `shots` and `won` are script-local `let` bindings that never become window properties (unlike LittleJS's own top-level `function` declarations) so arcade-bridge.js has no other way to observe them: window.__poolOnNewRack() at the end of newGame() (covers PLAY, NEW RACK and pause-menu QUIT, all of which route through newGame()) resets a once-per-rack report guard; window.__poolOnShot(shots) right after `shots++` in the strike-impulse branch of gameUpdate() reports a live score tick; window.__poolOnWin(shots) right after `won = true` in updateSinking() reports the final score and SDK.gameOver({won:true, score:shots}) exactly once per rack. No touch-drag bug found: verified with a scripted Playwright touch-drag (dispatched real TouchEvent touchstart/touchmove/touchend against the game's own canvas, Chromium hasTouch context) at 390x844 — LittleJS's document-level touch listeners (dist/littlejs.release.js, touchInputInit/handleTouch) already convert touch position through the same getBoundingClientRect-based mouseEventToScreen()/screenToWorld() path as mouse input, so mousePos/mouseIsDown/mouseWasReleased track the drag correctly and the game's own isTouchDevice-branched aim/commit logic (getAim(), canShoot(), the mouseWasReleased(0) commit in gameUpdate()) worked with no code changes; the cue stick visibly aimed opposite the drag and a shot fired on release, both on desktop mouse-drag and mobile touch-drag runs. No network calls, alert/confirm/prompt/window.open or personal names found anywhere in index.html or box2d.wasm.js (checked: box2d.wasm.js's only fetch/XHR/importScripts use is its own emscripten locateFile-derived same-directory .wasm load, confirmed resolving correctly with both files flat in this folder and the CSP's connect-src covering same-origin).
Comments 0
Log in to comment.