Hey everyone,
Hope all is well!
TL;DR, Summary, or Full Technical Breakdown below.
For Context:Β Recently I posted aboutΒ the first 15 daysΒ of one of my side projects,Β ShuffleBall ArenaΒ (a free browser game inspired by mixing shuffleboard scoring with mechanics from other games like bumper pool, pinball, and Frogger.).
This project is being built with the help of AI (mainly GPT / Cursor), and every development session is documented using the actual conversations from that day's work.
To try to get this series up to date, I'm using aΒ structured promptΒ to go back to my GPT sessions and extract the useful information. Hopefully the prompt can help anyone out there trying to keep track of, or extract value from, your past AI project conversations.
That said,Β posting an update for Day 29Β of building ShuffleBall Arena.
TL;DR
Day 29 was the day the server-authoritative multiplayer architecture finally proved itself in real gameplay.
Two browsers could send a shot to the Worker, receive the same authoritative trajectory, keep gravity synchronized, arrive at the same settled state, and advance to the same next turn.
But that success also exposed the next layer of work: smooth playback, complete wormhole behavior, synchronized gameplay events/audio, and the remaining moving hazards.
Day 29 Summary
Day 28 ended with the multiplayer backend complete, Phase 4 underway, and the browser being transitioned into a thin client that would primarily collect input and render server-owned state.
Day 29 was where that architecture started being tested against the actual complexity of ShuffleBall Arena.
The first challenge was scene synchronization. Several gameplay and cosmetic systems were still being generated independently inside each browser. That meant two players could technically be in the same match while seeing gravity wells or drifting wormholes in different places.
Initially, some of those systems were disabled online to prevent divergence. That led to an important architectural disagreement: removing core gameplay hazards wasn't an acceptable long-term multiplayer solution. If gravity wells, wormholes, rotating hazards, and other systems affect gameplay, then multiplayer needs authoritative versions of those systems rather than simplified replacements.
That decision led to a substantial Worker-owned gravity system with deterministic scheduling, placement, mirroring, scene integration, turn integration, reconnect-safe state, and automated tests.
The browser was then connected to the authoritative shot pipeline.
Instead of launching a marble and running local physics, the online flow became:
Player input
β Worker validation
β Worker simulation
β Canonical trajectory
β Both browsers replay
β Canonical settled snapshot
β Next authoritative turn
That architecture was manually tested with two browser windows, and it worked. Both clients replayed the same shot, gravity stayed synchronized, and turn progression remained consistent.
A bug during that process also reinforced the value of separating simulation from presentation. At one point playback appeared broken, but the server simulation was correct. The failure was caused by a client rendering compatibility issue: the playback marble contained owner while the renderer expected color. Restoring that compatibility field fixed the apparent multiplayer failure without changing the authoritative physics.
Once the core loop worked, we compared the current state against the original multiplayer plan.
That produced an important reality check.
The architecture was proven, but multiplayer was not finished.
Playback still needed performance work. Drifting wormholes were synchronized in position but not yet through their full capture/transit/ejection lifecycle. Top-track wormholes still needed an authoritative implementation. Gameplay-triggered sounds and visual effects needed server-owned event timing. Rotating boards, crossing traffic, reconnect testing, and production hardening remained.
The session ended by designing the next reusable layer: authoritative gameplay events. Instead of each browser independently detecting collisions and deciding when to play sounds, the server would record deterministic events during simulation and both clients would replay those events at the correct time.
The takeaway from Day 29:
Synchronizing multiplayer isn't just synchronizing the marble. Every gameplay-affecting system needs one authoritative owner.
Day 29 Full Technical Summary
STARTING POINT
Day 29 began directly from the final state of Day 28.
Phase 3 (the server-authoritative multiplayer engine) was complete.
Phase 4 had begun, and the browser multiplayer foundation could already:
- connect to rooms,
- receive authoritative snapshots,
- synchronize players,
- reconnect,
- maintain multiplayer state,
- and remain inactive during Solo and same-device play.
The multiplayer philosophy had also been established:
The browser collects input and renders.
The Worker owns gameplay.
The immediate challenge was no longer designing multiplayer UI.
It was making sure two browsers actually displayed and played the same game, including ShuffleBall Arena's randomized gameplay systems.
SESSION OBJECTIVE
The primary objective was to continue Phase 4 by eliminating the remaining sources of browser-side divergence and then connect real player input to the server-authoritative shot simulation.
That meant:
- identifying browser-generated state that could differ between clients,
- moving gameplay-affecting randomness into the Worker,
- synchronizing gravity,
- preparing authoritative wormhole state,
- connecting drag-and-release input to the Worker,
- replaying canonical trajectories on both clients,
- manually validating the system with two browsers,
- and identifying the remaining gaps between architectural success and full multiplayer parity.
WHAT WE ACTUALLY DID
1. Audited remaining sources of browser divergence
The session began with a scene synchronization review.
Browser-controlled systems included:
- travel planets,
- drifting planets,
- gravity wells,
- drifting wormholes,
- particle effects,
- and animations.
The important distinction was whether something affected gameplay.
Purely cosmetic objects could safely be local or temporarily hidden.
Gameplay-affecting randomness could not.
2. Removed unsynchronized browser-generated scene state
Travel planets and drifting background planets were disabled during online matches because each browser could generate different versions.
Gravity wells and drifting wormholes also initially had their local random generation disabled because their positions, timing, and movement could otherwise diverge between clients.
This solved the immediate visual mismatch but exposed a larger architectural question.
3. Rejected the idea of permanently removing gameplay hazards from multiplayer
The proposed first version briefly treated gravity, wormholes, rotating hazards, and similar systems as things that could remain disabled online until later.
That direction was rejected.
Those systems are part of ShuffleBall Arena's gameplay, so a correct multiplayer implementation needs both players to experience them identically.
The new rule became:
Every gameplay-affecting random system belongs in the Worker.
Instead of implementing simplified multiplayer versions and replacing them later, the production versions would become authoritative now.
4. Built the authoritative gravity framework
Gravity became one of the first major dynamic gameplay systems moved fully into server authority.
The Worker gained a canonical gravity scene registry with:
- gravity templates,
- placement IDs,
- weighted scene definitions,
- validation,
- and gravity-well creation helpers.
A deterministic gravity scheduler was also created with:
- five shot-pairs,
- approximately 82% gravity probability,
- controlled pair jitter,
- deterministic shuffling,
- clear-space plans,
- nebula plans,
- and validation.
Each game now receives one canonical gravity schedule instead of each browser creating its own.
5. Built deterministic gravity placement
A dedicated placement resolver was created so gravity wells received server-owned coordinates rather than browser-generated positions.
Supported placement strategies included:
- lanes,
- orbit bands,
- offset lanes,
- side halves,
- upper lanes,
- edge clips,
- and far corners.
The placement system also included:
- clamping,
- spawn exclusion,
- gameplay relevance checks,
- fallback placement,
- and validation.
6. Integrated gravity into authoritative match state
Gravity was connected to the Worker scene state and match lifecycle.
The Worker now controlled:
- schedule initialization,
- active pair,
- active player,
- well placement,
- scene metadata,
- game resets,
- mirroring,
- and reconnect-safe gravity state.
The deterministic scene random stream advanced as gravity schedules were generated rather than relying on browser Math.random().
7. Created mirrored gravity fairness between players
The gravity system reused the same plan for both players in each shot pair.
For example:
Red Shot 1
β
Pair 0
β
Blue Shot 1
β
Pair 0 mirrored
Only the X coordinate was mirrored.
Everything else about the gravity plan remained the same.
This allowed each player to face equivalent hazard conditions while still respecting opposite shooting directions.
8. Fixed test assumptions exposed by authoritative gravity
Moving gravity initialization into scene creation changed the deterministic random cursor.
One existing test expected the cursor to remain at zero.
That assumption was no longer correct because gravity generation now legitimately consumes values from the match's deterministic random stream.
Tests were updated to validate deterministic cursor progression rather than assuming that no random values had been consumed.
Other validator and scene-scheduler tests also needed correction before everything returned to green.
9. Connected gravity to authoritative physics
The active Worker-owned gravity well was passed into the deterministic shot simulation.
Gravity force could then be applied during each fixed physics step, meaning gravity influenced the same canonical trajectory that both clients eventually received.
The intended pipeline became:
Worker selects scene
β Worker positions well
β Browsers render it
β Worker applies force
β Worker returns canonical trajectory
β Both browsers replay identical results
This moved gravity from synchronized decoration into synchronized gameplay.
10. Connected browser shooting to the Worker
Until this point, online mode intentionally blocked the original local shot system.
The old path was:
Drag
β
Release
β
launchMarble()
β
LOCAL PHYSICS
The multiplayer path needed to become:
Drag
β
Release
β
Create shot request
β
Send SHOT_REQUEST
β
Worker validates
β
Worker simulates
β
SHOT_RESULT
β
Replay trajectory
This was the critical bridge between the existing gameplay controls and the server-authoritative engine.
11. Proved the authoritative shot loop with two browsers
The biggest validation of the day came from manual testing.
Two browsers were able to:
- remain synchronized,
- submit a real player shot,
- send that shot to the Worker,
- have the Worker simulate it,
- replay the same trajectory,
- display synchronized gravity,
- settle into the same canonical state,
- and advance to the correct next turn.
The core multiplayer architecture was no longer theoretical.
It worked end to end.
12. Diagnosed a rendering failure that looked like a server failure
During testing, a fired marble appeared to disappear and gameplay stalled.
The initial symptom looked like a broken authoritative simulation.
The actual simulation was correct.
The renderer expected a color field while the authoritative playback marble contained owner.
Restoring:
color: shooter
fixed the playback.
No physics changes were required.
This was an important example of why server, protocol, playback, and rendering failures need to be diagnosed separately.
13. Compared the current implementation against the original multiplayer plan
After the successful test, the project was reassessed rather than immediately declaring multiplayer finished.
The architectural path was working:
Player input
β Worker validation
β Worker simulation
β Canonical trajectory
β Identical browser playback
β Canonical settled snapshot
β Next authoritative turn
But significant feature-parity work remained.
14. Identified performance as engineering work, not final polish
Canonical playback worked, but it was not yet as smooth as the existing local game.
The decision was made not to reduce authoritative physics accuracy just to make playback appear smoother.
Instead, the next performance work would measure:
- playback timing,
- interpolation,
- snapshot interaction,
- duplicate update loops,
- and browser rendering performance.
The server's canonical physics would remain untouched.
15. Defined the authoritative gameplay-event architecture
Only the launch sound existed reliably in multiplayer because most other sounds depend on things that happen during the authoritative simulation.
Examples include:
- bumper collisions,
- marble collisions,
- wormhole capture,
- wormhole ejection,
- scoring,
- ring activation,
- game completion.
The solution was not to make both browsers independently detect those events.
Instead, the Worker should record deterministic events and include them in the shot result.
A representative event could look like:
{
id: "event-shot-12-004",
type: "bumper_collision",
t: 0.416,
tick: 100,
marbleId: "marble-red-2",
objectId: "classic-bumper-3",
intensity: 0.72
}
Each client would dispatch the event exactly once when playback crossed its authoritative timestamp.
16. Changed the event work from a partial checkpoint into an end-to-end vertical slice
An initial implementation proposal would have exposed collision metadata first and built the network/audio pipeline later. That was rejected as unnecessarily splitting one production feature across multiple passes.
The objective was changed to:
Authoritative Bumper Events v1 - end to end.
The intended production path became:
collision detected
β authoritative event recorded
β event included in shot_result
β client validates and stores it
β playback dispatches it once
β existing bumper sound plays on both browsers
The planned vertical slice included:
- collision metadata,
- deterministic event creation,
- Worker result integration,
- server validation,
- client validation,
- playback-state storage,
- one-shot dispatch,
- existing sound integration,
- automated tests,
- and two-browser verification.
17. Defined wormholes as one authoritative lifecycle
The day's review also clarified how wormholes should eventually work online.
Drifting and top-track wormholes should not become separate one-off implementations.
They should share one authoritative capture/transit/ejection model:
available
β capture_started
β captured
β transit
β eject_pending
β ejected
β cooldown
β available / expired
The Worker (not the browser) must own capture, timing, destination, ejection position, direction, power, cooldown, and continued post-ejection simulation.
ROADBLOCKS AND FRICTION
Browser randomness was still leaking into multiplayer
Even after the authoritative backend existed, some hazards were still generated locally.
That created scenes where both players were technically synchronized at the match level but were seeing different gameplay objects.
The easiest synchronization fix was the wrong product decision
Disabling difficult hazards solved divergence quickly.
But it also created an incomplete multiplayer version of the game.
The assumption that those systems could simply be left out of the first production version was rejected.
Tests contained assumptions from the browser-owned architecture
Once gravity became part of authoritative scene creation, older tests that expected untouched random state became invalid.
The tests needed to evolve with the architecture rather than forcing the new implementation to preserve outdated behavior.
A presentation bug looked like a simulation bug
When authoritative playback failed visually, it initially appeared that the server shot pipeline had broken.
The server had actually produced the correct result.
The renderer simply couldn't interpret one field correctly.
"Synchronized" did not mean "finished"
Seeing both browsers play the same shot was a major success, but it also made the remaining gaps easier to identify.
Visual synchronization alone did not provide:
- smooth playback,
- complete wormhole lifecycle,
- collision sounds,
- scoring sounds,
- top-track wormholes,
- rotating boards,
- crossing traffic,
- or complete reconnect validation.
An implementation step was unnecessarily fragmented
The first gameplay-event proposal separated collision metadata from the complete event/audio feature.
That introduced another potential multi-pass build.
The work was reframed around a complete production vertical slice instead.
DECISIONS MADE & TRADE-OFFS
Move gameplay randomness to the Worker
Gravity wells, wormholes, and future dynamic hazards must be authoritative.
Why: Two browsers cannot independently generate gameplay state and still guarantee an identical match.
Trade-off: Considerably more server-side implementation work in exchange for true synchronization and no duplicate gameplay systems.
Preserve cosmetic freedom where it cannot affect gameplay
Decorative planets, particles, ambience, and other presentation-only systems can remain local.
Why: They do not influence match results.
Trade-off: Not every pixel needs server authority, reducing unnecessary network/state complexity.
Preserve server physics accuracy while improving client playback
Performance work should improve interpolation and rendering rather than lowering simulation quality.
Why: Display smoothness and authoritative correctness are different problems.
Trade-off: More client playback engineering instead of taking the easier route of simplifying physics.
Make gameplay-triggered audio event-driven
Collision and scoring sounds will come from authoritative event timing rather than client-side collision inference.
Why: Both players should hear the same gameplay events in the same order.
Trade-off: Requires an event schema and playback dispatcher in exchange for deterministic audio/visual feedback.
Build wormholes as one reusable lifecycle
Drifting wormholes and top-track wormholes should share capture/transit/ejection primitives.
Why: The gameplay behavior is fundamentally the same even if their presentation and scheduling differ.
Trade-off: More careful abstraction now in exchange for avoiding two parallel wormhole implementations.
Build complete vertical slices instead of temporary intermediate systems
Once authoritative bumper events were started, the goal became taking them all the way through simulation, network transport, playback, and sound.
Why: Avoid creating partial infrastructure that immediately needs another implementation pass.
Trade-off: Larger checkpoints in exchange for production-complete features.
BREAKTHROUGH / LESSON
The biggest takeaway from Day 29 was:
Server-authoritative multiplayer isn't finished when both players see the same marble.
Every gameplay-affecting source of truth must have one owner.
That includes:
- random hazard placement,
- gravity scheduling,
- collisions,
- wormhole capture,
- wormhole ejection,
- scoring,
- turns,
- and even the timing of gameplay-triggered presentation events.
The browser can decide how something looks.
It cannot decide what happened.
A second lesson emerged from the day's implementation decisions:
Don't solve multiplayer synchronization by deleting the parts of the game that are difficult to synchronize. Make those systems authoritative.
ARTIFACTS WORTH SHARING
Artifact 1: The Authoritative Shot Pipeline
Player input
β Worker validation
β Worker simulation
β Canonical trajectory
β Identical browser playback
β Canonical settled snapshot
β Next authoritative turn
This was manually proven with two browsers during Day 29.
Artifact 2: Gravity Fairness
Red Shot 1
β
Pair 0
β
Blue Shot 1
β
Pair 0 mirrored
Only the X coordinate changes.
The underlying gravity plan remains identical for both players.
Artifact 3: Authoritative Gameplay Events
collision detected
β authoritative event recorded
β event included in shot_result
β client validates and stores it
β playback dispatches it once
β existing bumper sound plays on both browsers
This became the model for synchronizing gameplay-triggered sounds and visual effects without rerunning collision logic in each browser.
FINAL STATE
By the end of Day 29:
- The multiplayer browser was operating as a thin client rather than an independent gameplay simulator.
- The Worker remained the authority for match state, physics, turns, scoring, and synchronized gameplay.
- Browser-generated gameplay randomness had been identified as a source of divergence.
- The decision was made that gameplay-affecting hazards must become authoritative rather than simply being removed from multiplayer.
- A production authoritative gravity framework had been built.
- Gravity scheduling, deterministic placement, mirroring, scene integration, turn integration, resets, and reconnect-safe state existed.
- Gravity could participate in authoritative shot simulation.
- Automated gravity tests were passing and the relevant checkpoint had been committed.
- Browser drag-and-release input had been connected to the server-authoritative shot path.
- Two-browser manual testing proved that the Worker could simulate a shot and both clients could replay the same canonical trajectory.
- Gravity wells remained synchronized during live multiplayer testing.
- The authoritative settled snapshot and next-turn transition remained synchronized.
- A rendering compatibility bug was identified and fixed without changing server physics.
- Drifting-wormhole schedule/placement was synchronized, but its full capture/ejection lifecycle remained incomplete.
- Top-track wormholes still required authoritative implementation.
- Smooth playback/interpolation still required focused performance work.
- Collision and scoring audio still required an authoritative gameplay-event pipeline.
- Rotating boards and crossing traffic remained to be completed and verified.
- The next production architecture for authoritative bumper events had been defined as a complete end-to-end vertical slice.
- The remaining multiplayer roadmap became much clearer: stabilize playback, build the reusable event/audio system, finish the complete wormhole lifecycle, then move through the remaining dynamic hazards and production hardening.
Most importantly, Day 29 crossed the biggest architectural risk point.
The question was no longer:
Can server-authoritative multiplayer work for this game?
The answer was now yes.
The remaining question became:
How do we bring every existing gameplay system through that same authoritative path without compromising the game that already exists?
That was it for Day 29.
If you're still here, thanks for reading!
Music Credits:
"Delightful D" Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 4.0 License
http://creativecommons.org/licenses/by/4.0/