Building JavaScript-Driven Game Buttons in West Marches Game
Modern web apps — especially games — live or die by responsiveness.
Click → wait → page reload is fine for CRUD dashboards.
It’s terrible for gameplay.
In my latest commit to West Marches Game, I moved several core game interactions away from traditional full-page redirects and toward JavaScript-driven actions.
👉 Commit: “javascript game buttons” View Commit on GitHub
This post walks through what changed, why it matters, and how small controller tweaks unlock much richer UX.
The Problem: Every Action Reloaded the Page
Originally, game interactions behaved like classic MVC applications:
- Delete a poll slot → redirect
- Change player status → redirect
- Modify game state → redirect
Every action ended with:
return $this->redirect([$url]);
Technically correct.
But functionally painful.
From a player’s perspective:
- Click button
- Entire page reloads
- Scroll position lost
- Context broken
- Game feels slow
For something meant to feel interactive, this was a mismatch.
The Goal: Buttons That Feel Like a Game
The objective was simple:
Game buttons should behave like game controls — not admin forms.
That means:
- Instant feedback
- No page reloads
- Background server updates
- Progressive enhancement (still works without JS)
The Key Idea: Dual-Mode Controller Actions
Instead of creating new endpoints, I upgraded existing ones.
New Pattern
Controller actions now accept a $js flag:
public function actionPlayerstatus($id, $campaignId, $gamePlayerId, $js = false)
and
public function actionPollslotdelete($id, $campaignId, $slotId, $js = false)
This tiny change unlocked two execution modes:
| Mode | Behavior |
|---|---|
| Normal request | Redirect as before |
| JavaScript request | Return simple success response |
The Core Change
Previously:
return $this->redirect([$url]);
Now:
return ($js) ? "true" : $this->redirect([$url]);
That’s it.
But architecturally, it’s huge.
The same endpoint now supports:
- traditional navigation
- AJAX interaction
No duplicated logic.
No parallel APIs.
No rewrite.
Why This Works So Well
1. Progressive Enhancement
If JavaScript fails:
✅ App still works
✅ Controllers still redirect
✅ No broken flows
JavaScript becomes an enhancement — not a requirement.
2. Zero Business Logic Duplication
A common mistake:
/delete→ redirect controller/api/delete→ AJAX controller
Now we have one source of truth.
All validation stays centralized:
if (empty($player)) {
return ($js) ? "true" : $this->redirect([$url]);
}
3. Cleaner Frontend Integration
JavaScript buttons can now do:
$.post('/game/playerstatus?...&js=1')
Then simply:
- update UI
- toggle icons
- remove rows
- refresh partial sections
No navigation required.
Where This Was Applied
This commit touched multiple gameplay surfaces:
- Poll slot deletion
- Player status toggling
- Event-driven game interactions
- Partial view updates
Six files changed overall, introducing JavaScript-aware workflows across controllers and views. (GitHub)
The UX Difference
Before:
Click → reload → wait → find your place again.
After:
Click → state updates instantly.
The application finally behaves like a live session manager, not a static web form.
An Unexpected Benefit: Backend Simplicity
Ironically, adding JavaScript made the backend simpler.
Instead of designing a full REST API layer, we:
- reused MVC routes
- added one optional parameter
- returned minimal responses
Sometimes the cleanest solution is not adding architecture — but making existing architecture flexible.
Design Principle: Controllers Should Speak Two Languages
A powerful takeaway from this change:
Controllers should handle both navigation and interaction.
Think of them as bilingual:
- HTML for humans
- lightweight responses for JavaScript
You don’t always need a separate API to build interactive systems.
What Comes Next
This commit lays the groundwork for bigger improvements:
- inline updates
- live campaign management
- reactive UI components
- eventually real-time gameplay features
The game now has controls instead of links — and that changes everything.