Build a Memory Game with ClojureScript & Scittle
memory-game, simon, browser-game, web-audio-api, reagent-atoms, game-state, functional-programming
Build a Memory Game with ClojureScript & Scittle
About This Project
Ever wanted to build an interactive browser game without dealing with build tools, webpack, or complicated setups? Today, I’ll show you how to create a Simon-style memory game using Scittle and ClojureScript that runs entirely in your browser!
This is part of my ongoing exploration of browser-native development with Scittle. Check out my previous articles in this series:
- Building Browser-Native Presentations with Scittle - Create interactive presentation systems without build tools
- Python + ClojureScript: Pyodide Integration with Scittle - Run Python code directly in the browser
- Free Weather Data with National Weather Service API - Build weather applications using the free NWS API
- Browser-Native QR Code Scanner with Scittle - Build a QR code scanner that runs entirely in your browser
This project demonstrates how to build fully interactive games with zero build configuration.
What We’re Building
We’ll create a classic memory game (similar to Simon) where:
- The computer shows a sequence of colors
- Each color plays a unique musical note
- Players must repeat the sequence correctly
- The sequence grows longer with each successful round
- The game tracks your high score
All of this with keyword arguments for cleaner, more readable code and Web Audio API for sound effects!
Why Scittle for Games?
Zero Build Configuration
Traditional game development in ClojureScript requires:
- Setting up shadow-cljs or figwheel
- Managing dependencies
- Configuring build tools
- Dealing with hot reload setup
With Scittle, you just write ClojureScript and it runs. Perfect for prototyping, learning, or building simple games.
Instant Feedback
Changes appear immediately when you save your file. No waiting for builds, no watching compile output - just pure development flow.
Educational Value
This approach is perfect for:
- Teaching ClojureScript concepts
- Learning game state management
- Understanding React/Reagent patterns
- Exploring Web APIs (Audio, Animation)
Key Concepts We’ll Cover
1. State Management with Reagent Atoms
We use a single atom to manage all game state:
(def game-state (r/atom {:sequence [] ; Computer's sequence
:player-sequence [] ; Player's input
:playing? false ; Game active?
:showing? false ; Showing sequence?
:score 0 ; Current level
:game-over? false ; Game over?
:active-tile nil ; Currently lit tile
:high-score 0})) ; Best score2. Web Audio API Integration
Creating dynamic sound effects without audio files:
(defn play-tone
[& {:keys [frequency duration]
:or {frequency 440 duration 0.2}}]
(when audio-context
(let [oscillator (.createOscillator audio-context)
gain-node (.createGain audio-context)]
(.connect oscillator gain-node)
(.connect gain-node (.-destination audio-context))
(set! (.-value (.-frequency oscillator)) frequency)
(set! (.-value (.-gain gain-node)) 0.3)
(.start oscillator)
(.stop oscillator (+ (.-currentTime audio-context) duration)))))3. Keyword Arguments Pattern
All functions use keyword arguments for clarity:
;; Instead of: (play-tone 440 0.5)
;; We write:
(play-tone :frequency 440 :duration 0.5)
;; Component props are also keyword-based:
[color-tile :color "#4caf50"
:index 0
:on-click handle-click
:disabled? false]Game Architecture
The game follows a simple but effective architecture:
Core Game Mechanics
Sequence Generation
Each round adds a random color to the sequence:
(defn add-to-sequence []
(let [next-color (rand-int 4)]
(swap! game-state update :sequence conj next-color)))Visual and Audio Feedback
The game shows the sequence with synchronized visuals and sounds:
(defn show-sequence []
(doseq [[idx color-idx] (map-indexed vector sequence)]
(js/setTimeout
(fn []
(swap! game-state assoc :active-tile color-idx)
(play-tone :frequency (nth color-frequencies color-idx)
:duration 0.5)
(js/setTimeout
#(swap! game-state assoc :active-tile nil)
400))
(* idx 600))))Input Validation
Player inputs are validated immediately:
(defn handle-tile-click [& {:keys [tile-index]}]
(let [current-position (dec (count player-sequence))]
(cond
;; Wrong input - game over
(not= (nth player-sequence current-position)
(nth sequence current-position))
(do (swap! game-state assoc :game-over? true)
(play-tone :frequency 100 :duration 0.5))
;; Sequence complete - next level
(= (count player-sequence) (count sequence))
(do (swap! game-state update :score inc)
(add-to-sequence)
(show-sequence)))))UI Components with Inline Styles
Each component is self-contained with its own styles:
Color Tiles
Interactive tiles with hover effects:
(defn color-tile [& {:keys [color index on-click disabled?]}]
[:div {:style {:width "100px"
:height "100px"
:background-color color
:transform (if active? "scale(1.1)" "scale(1)")
:transition "all 0.2s ease"}
:on-mouse-enter (fn [e] ...)
:on-mouse-leave (fn [e] ...)}])Score Display
Dynamic score with color changes:
(defn score-display [& {:keys [playing? game-over? score]}]
[:h3 {:style {:color (cond
game-over? "#ff4444"
playing? "#4caf50"
:else "#333")}}
(cond
game-over? (str "Game Over! Score: " score)
playing? (str "Level: " (inc score))
:else "Press Start to Play")])Learning Points
This project teaches several important concepts:
1. Functional Game Development
- Pure functions for game logic
- Immutable state management
- Reactive UI updates with atoms
2. Browser API Integration
- Web Audio API for dynamic sounds
- CSS transitions for animations
- Event handling for user input
3. ClojureScript Patterns
- Keyword arguments for readability
- Component composition
- State atom patterns
Try the Game!
The complete game is embedded below. Click “Start Game” to begin!
Extending the Game
Here are some ideas to enhance the game:
1. Difficulty Levels
- Easy: Slower sequence display
- Medium: Current speed
- Hard: Faster sequence, more colors
2. Visual Themes
- Different color schemes
- Shape variations
- Animation styles
3. Sound Options
- Different instrument sounds
- Musical scales
- Volume control
4. Game Modes
- Reverse mode (play sequence backwards)
- Speed mode (time limits)
- Pattern mode (specific patterns to memorize)
5. Persistence
- Local storage for high scores
- Player profiles
- Statistics tracking
Key Takeaways
Building games with Scittle and ClojureScript offers:
- Rapid Prototyping - No build setup means faster iteration
- Clean Code - Functional programming keeps logic simple
- Browser-Native - Runs anywhere without servers
- Educational - Perfect for learning and teaching
- Fun! - Immediate feedback makes development enjoyable
Resources and Links
Conclusion
This memory game demonstrates that you don’t need complex build tools to create interactive browser applications. With Scittle, ClojureScript, and Reagent, you can build engaging games that run directly in the browser.
The combination of functional programming, reactive state management, and browser APIs creates a powerful platform for game development. Whether you’re learning ClojureScript, teaching programming concepts, or just having fun, this approach offers a refreshing alternative to traditional web development.
Happy coding, and see how high you can score! 🎮
Want to see more browser-native ClojureScript projects? Check out my other articles on ClojureCivitas where we explore the boundaries of what’s possible without build tools!