Notes / January 30, 2026

Shamrock Stakes: a Laravel prediction-market party app

Laravel Cloud, broadcasting, and real-time odds for low-stakes “markets” at company events.

Shamrock Stakes is an internal event app: employees bet play money on silly, day-long outcomes (“Will someone mention Kubernetes before lunch?”). No real money changes hands—the goal is engagement, ice-breaking, and a harmless competitive vibe.

Technically it is a small Laravel + realtime product with the same discipline as customer software: auth, auditability, and deployment that survives one noisy Wi‑Fi.


1. Product primitives

Model Responsibility
Party Event container: time window, branding, invite list
Market One question; binary or small multi-outcome
Outcome Possible resolutions for a market
Position User stake on an outcome (integer chips)
LedgerEntry Append-only accounting for bankroll changes

Settlement

  • Admin closes market → job locks rows (SELECT … FOR UPDATE or DB transaction) → computes payouts from parimutuel or fixed-odds rules (we used simple house rules, documented in UI).
  • Writes ledger lines + broadcasts UI refresh.

2. Realtime: Laravel Echo + Reverb

Channels

  • party.{id} — roster + “markets added” events (PartyMarketsUpdated)
  • market.{uuid} — odds / pool updates (MarketOddsUpdated)

Storefront blade pattern

  • Inline script waits for window.Echo (loaded from Vite bundle) with bounded retries—conference Wi‑Fi often delays JS.
  • Subscribe per visible market card; unsubscribe on turbo:before-visit / page unload if using Turbo-like navigation.

Server

  • Events implement ShouldBroadcast; use broadcast(new MarketOddsUpdated(...))->toOthers() when appropriate to avoid echo loops.

Auth

  • PrivateChannel / PresenceChannel if you need “who is online”—we kept public party channels + server-side authorization on mutating routes.

3. Laravel Cloud / deployment

  • TLS everywhere—mixed content blocks WS.
  • Environment: REVERB_APP_ID, keys, VITE_REVERB_* must match between Pusher-compatible client config and server.
  • Horizontal scale: sticky sessions not required for API if JWT/Sanctum; websockets need Reverb scaled with Redis adapter—document concurrent connection limits for the venue.

4. UX safeguards

  • Max stake per market and per user to prevent one person “ending the joke.”
  • Explain this is not gambling in copy + email footers.
  • Admin undo window before settlement finalizes—mistakes happen on stage.

5. Testing

  • Feature tests on settlement math (property-based tests for chip conservation: sum before = sum after).
  • Load test a fake burst of broadcasts locally with artisan websockets:serve + k6 hitting HTTP endpoints (not WS deep load—good enough smoke).

6. What we learned

  • Projector screens need large fonts and high-contrast odds—design for the back row, not the laptop.
  • Echo fallback: if websocket fails, poll /api/parties/{id}/snapshot every N seconds—ugly but saves the event.

Resume framing

“Shipped Laravel + Reverb/Echo realtime for an internal prediction-market experience: broadcast design, settlement transactions, resilient client subscribe, and cloud-friendly deploy.”