Architecture and decisions
Casino math and graphical immersion
A digital card game requires absolute mathematical correctness in game logic and smooth performance in presentation. The goal was building a 1951 Flamingo-era film-noir blackjack table rendered in 3D, but visual styling is meaningless if a card split corrupts table state.
State graph explosion inside React hooks
The prototype managed table state directly within React components using distributed useState and useEffect hooks. As rules expanded to accommodate split hands, insurance wagers, double downs, surrender options, and dealer soft-17 checks, state transitions became tied to component re-render schedules. A scenario involving simultaneous split aces with dealer blackjack evaluation triggered concurrent hook updates. The component re-rendered in an invalid state, freezing the WebGL canvas and corrupting the player balance.
A zero-dependency pure reducer
I extracted the entire game engine out of React into a pure TypeScript state machine spanning 782 lines and 14 action types. The reducer receives an immutable state snapshot and an action payload, computing the subsequent table state with zero browser or UI dependencies. Card distribution uses a Fisher-Yates shuffle seeded with cryptographic randomness. React Three Fiber acts as a passive presentation layer that renders card meshes, chip stacks, table felt textures, and ACES filmic tone mapping based purely on state snapshots.
Deterministic game logic and strategy analysis
The reducer is independent of the DOM, which keeps rule evaluation deterministic and testable. An integrated strategy evaluator compares player decisions with mathematically optimal play at runtime, tracking accuracy across hard totals, soft totals, pair splits, and surrender scenarios. I do not use the production-delivery test as a proxy for engine throughput.
Measured production delivery
I added a k6 production-load harness with Grafana and InfluxDB to measure the deployed web-delivery path. In a 3 minute 5 second ramp to 500 concurrent virtual users, the live application completed 59,635 requests at 320.41 requests per second. Every response was HTTP 200; request failures were 0.00%; p95 latency was 62.18 ms and p99 latency was 100.63 ms.
The harness records exact response-status counts alongside latency, throughput, virtual users, and failure rate. Those measurements demonstrate the delivery path under concurrency. They do not measure WebGL frame time, canvas stability, input latency, or the reducer's simulation throughput; those are separate benchmarks.
Architecture discipline: logic before mood
I spent two weeks tuning film-noir scene lighting and 3D chip physics before the underlying rules engine was stable. That was an operational mistake. In systems engineering, verify state transitions and core invariants first; presentation polish should always build on a proven engine.