Memory Game Tutorial
CoderKit Team
January 4, 2026
Posted: January 2026 | Reading time: 14 minutes | Platform: CoderKit Mobile IDE
You want to build games, but you want to see them right on your phone screen.
This tutorial teaches you web-based game development using HTML, CSS, and JavaScript—all running live in CoderKit's integrated web server.
CoderKit Superpower: Run a full Flask web server on your phone. Build, test, and deploy web games without a computer.
By Friday, you'll have a playable Memory Card Game that runs in your phone's browser. You'll understand:
- DOM manipulation (HTML from JavaScript)
- Event listeners (touch and click)
- Game state management
- CSS animations
- Making it mobile-friendly
Time required: 6-8 hours total (1-2 hours/day, Monday-Friday)
Difficulty: Beginner to Intermediate
Prerequisites: Basic HTML, CSS, JavaScript (or willingness to learn alongside)
Download CoderKit Free → Everything you need is built-in.
The Game Plan
What we're building:
A Memory Card Game where:
- 12 cards (6 matching pairs)
- Flip two cards at a time
- Match all pairs
- Score based on moves
[?] [?] [?] [?]
[?] [?] [?] [?]
[?] [?] [?] [?]
Moves: 0 Matched: 0
Flip a card → See emoji → Flip another → Match or no match
Features:
- Touch/click cards
- Animated flip effect
- Move counter
- Win detection
- Difficulty levels (easy, medium, hard)
Total code: ~250 lines (150 HTML+CSS, 100 JavaScript)
Why Web Games on Your Phone? (Only on CoderKit)
Problem: Building games requires a laptop + complex development tools + struggle with setup.
CoderKit Solution: Run a complete web development environment on your phone.
What makes this possible:
- ✅ Full Python + Flask support in CoderKit
- ✅ Integrated web server (no localhost config needed)
- ✅ Live browser preview on same device
- ✅ File management built-in
- ✅ One-click execution
You can:
- Code HTML/CSS/JavaScript in CoderKit
- Serve it with Flask running on your phone
- View in browser (same phone, real-time!)
- Deploy to web instantly
This is professional web development—no laptop required. The same code runs on your phone, tablet, desktop, or the live web.
File Structure
In CoderKit, create three files:
memory_game/
├── app.py (Flask server)
├── index.html (Game HTML)
└── game.js (Game logic)
The app.py serves index.html, which loads game.js.
Step 1: Create Flask Server (Monday)
File: app.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True, host='127.0.0.1', port=5000)
What it does:
- Creates a web server
- Serves
index.htmlwhen you visithttp://localhost:5000 - Runs on your phone
To run:
- Create
app.py - Run it in CoderKit
- Open
http://localhost:5000in browser
Step 2: Build the HTML (Monday-Tuesday)
File: templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Memory Card Game</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.container {
background: white;
border-radius: 20px;
padding: 30px;
max-width: 500px;
width: 100%;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
}
h1 {
text-align: center;
color: #333;
margin-bottom: 10px;
font-size: 28px;
}
.stats {
display: flex;
justify-content: space-around;
margin-bottom: 30px;
padding: 15px;
background: #f0f0f0;
border-radius: 10px;
}
.stat {
text-align: center;
}
.stat-label {
font-size: 12px;
color: #666;
text-transform: uppercase;
margin-bottom: 5px;
}
.stat-value {
font-size: 24px;
font-weight: bold;
color: #667eea;
}
.difficulty {
display: flex;
gap: 10px;
margin-bottom: 20px;
justify-content: center;
}
.difficulty button {
padding: 8px 16px;
border: 2px solid #ddd;
background: white;
border-radius: 8px;
cursor: pointer;
font-weight: bold;
transition: all 0.3s;
}
.difficulty button.active {
background: #667eea;
color: white;
border-color: #667eea;
}
.difficulty button:hover {
border-color: #667eea;
}
.game-board {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 15px;
margin-bottom: 20px;
}
.card {
aspect-ratio: 1;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
border-radius: 12px;
cursor: pointer;
font-size: 40px;
display: flex;
justify-content: center;
align-items: center;
transition: all 0.3s;
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
position: relative;
transform-style: preserve-3d;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 16px rgba(0,0,0,0.3);
}
.card.flipped {
background: #f0f0f0;
color: #333;
}
.card.matched {
background: #4caf50;
color: white;
cursor: default;
opacity: 0.7;
}
.card.matched:hover {
transform: none;
}
.buttons {
display: flex;
gap: 10px;
justify-content: center;
}
button {
padding: 12px 24px;
font-size: 16px;
border: none;
border-radius: 8px;
cursor: pointer;
font-weight: bold;
transition: all 0.3s;
}
.btn-reset {
background: #667eea;
color: white;
}
.btn-reset:hover {
background: #5568d3;
transform: scale(1.05);
}
.message {
text-align: center;
margin-top: 20px;
padding: 15px;
border-radius: 8px;
font-weight: bold;
display: none;
}
.message.show {
display: block;
}
.message.win {
background: #c8e6c9;
color: #2e7d32;
}
@media (max-width: 600px) {
.container {
padding: 20px;
}
h1 {
font-size: 24px;
}
.game-board {
gap: 10px;
}
.card {
font-size: 32px;
}
}
</style>
</head>
<body>
<div class="container">
<h1>🎮 Memory Game</h1>
<div class="stats">
<div class="stat">
<div class="stat-label">Moves</div>
<div class="stat-value" id="moves">0</div>
</div>
<div class="stat">
<div class="stat-label">Matched</div>
<div class="stat-value" id="matched">0/6</div>
</div>
<div class="stat">
<div class="stat-label">Best</div>
<div class="stat-value" id="best">-</div>
</div>
</div>
<div class="difficulty">
<button class="active" onclick="setDifficulty('easy')">Easy</button>
<button onclick="setDifficulty('medium')">Medium</button>
<button onclick="setDifficulty('hard')">Hard</button>
</div>
<div class="game-board" id="gameBoard"></div>
<div class="message" id="message"></div>
<div class="buttons">
<button class="btn-reset" onclick="resetGame()">New Game</button>
</div>
</div>
<script src="{{ url_for('static', filename='game.js') }}"></script>
</body>
</html>
Concepts:
- Semantic HTML structure
- CSS Grid for card layout
- Flexbox for alignment
- CSS custom properties (colors)
- Responsive design (mobile-first)
- CSS transitions (smooth animations)
Step 3: Write Game Logic in JavaScript (Tuesday-Wednesday)
File: static/game.js
// Game state
let gameState = {
cards: [],
flipped: [],
matched: [],
moves: 0,
difficulty: 'easy',
isLocked: false,
bestScore: localStorage.getItem('bestScore') || '-'
};
// Emoji pairs
const emojis = {
easy: ['🍎', '🍌', '🍊', '🍇', '🍓', '🍒'],
medium: ['🍎', '🍌', '🍊', '🍇', '🍓', '🍒', '🍑', '🥝'],
hard: ['🍎', '🍌', '🍊', '🍇', '🍓', '🍒', '🍑', '🥝', '🥭', '🍍']
};
// Initialize game
function initGame() {
const selectedEmojis = emojis[gameState.difficulty];
// Create pairs: [🍎, 🍎, 🍌, 🍌, ...]
gameState.cards = [...selectedEmojis, ...selectedEmojis];
// Shuffle
shuffle(gameState.cards);
// Reset state
gameState.flipped = [];
gameState.matched = [];
gameState.moves = 0;
gameState.isLocked = false;
// Render
renderBoard();
updateStats();
}
// Shuffle array (Fisher-Yates)
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
// Render game board
function renderBoard() {
const board = document.getElementById('gameBoard');
board.innerHTML = '';
gameState.cards.forEach((emoji, index) => {
const card = document.createElement('button');
card.className = 'card';
card.textContent = '?';
card.onclick = () => flipCard(index);
// Show if flipped or matched
if (gameState.flipped.includes(index) || gameState.matched.includes(index)) {
card.classList.add('flipped');
card.textContent = emoji;
}
if (gameState.matched.includes(index)) {
card.classList.add('matched');
}
board.appendChild(card);
});
}
// Flip a card
function flipCard(index) {
// Can't flip if already flipped, matched, or locked
if (gameState.flipped.includes(index) ||
gameState.matched.includes(index) ||
gameState.isLocked) {
return;
}
// Add to flipped
gameState.flipped.push(index);
renderBoard();
// If two cards flipped, check match
if (gameState.flipped.length === 2) {
gameState.isLocked = true;
gameState.moves++;
checkMatch();
}
}
// Check if two cards match
function checkMatch() {
const [first, second] = gameState.flipped;
const match = gameState.cards[first] === gameState.cards[second];
setTimeout(() => {
if (match) {
// Match!
gameState.matched.push(first, second);
}
// Reset flipped
gameState.flipped = [];
gameState.isLocked = false;
// Render
renderBoard();
updateStats();
// Check win
if (gameState.matched.length === gameState.cards.length) {
winGame();
}
}, 600);
}
// Update stats display
function updateStats() {
document.getElementById('moves').textContent = gameState.moves;
document.getElementById('matched').textContent =
`${gameState.matched.length / 2}/${gameState.cards.length / 2}`;
document.getElementById('best').textContent = gameState.bestScore;
}
// Win game
function winGame() {
const message = document.getElementById('message');
message.classList.add('show', 'win');
message.textContent = `🎉 You won in ${gameState.moves} moves!`;
// Update best score
if (gameState.bestScore === '-' || gameState.moves < parseInt(gameState.bestScore)) {
gameState.bestScore = gameState.moves;
localStorage.setItem('bestScore', gameState.moves);
updateStats();
}
}
// Set difficulty
function setDifficulty(level) {
gameState.difficulty = level;
// Update buttons
document.querySelectorAll('.difficulty button').forEach(btn => {
btn.classList.remove('active');
});
event.target.classList.add('active');
// Reset grid columns
const gridCols = {
easy: 3,
medium: 4,
hard: 5
};
document.getElementById('gameBoard').style.gridTemplateColumns =
`repeat(${gridCols[level]}, 1fr)`;
// New game
resetGame();
}
// Reset game
function resetGame() {
document.getElementById('message').classList.remove('show', 'win');
initGame();
}
// Start on page load
window.onload = initGame;
Concepts:
- Object to manage game state
- Array methods (push, includes, slice)
- DOM manipulation (getElementById, createElement, textContent)
- Event listeners (onclick)
- setTimeout (delay)
- localStorage (persistent data)
- Array spreading (...)
- Destructuring assignment
- Arrow functions
File Structure in CoderKit
project/
├── app.py
├── templates/
│ └── index.html
└── static/
└── game.js
In CoderKit (this is how simple it is):
- Create
app.pywith Flask code - Create
templatesfolder, addindex.html - Create
staticfolder, addgame.js - Run
app.py - Open browser:
http://localhost:5000
Complete app.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True, host='127.0.0.1', port=5000)
Step-by-Step Implementation (CoderKit Mobile)
Monday (1.5 hours)
- Create
app.pywith Flask - Create folder structure
- Create
index.html(up to<style>) - Test: Run app, see page load
Tuesday (2 hours)
- Complete
index.html(HTML + CSS) - Create
static/game.js(first half, init & shuffle) - Test: Cards display in grid
Wednesday (2 hours)
- Complete
game.js(flip logic, match checking) - Test: Flip cards, see matching work
- Debug: Fix any layout issues
Thursday (1.5 hours)
- Add difficulty levels
- Add score tracking
- Test all features
- Polish animations
Friday (1 hour)
- Share with friends
- Screenshot for portfolio
- Deploy to web (optional)
Next: Test your game in CoderKit's browser.
Testing Checklist
- ✅ Flask server starts
- ✅ Page loads at
http://localhost:5000 - ✅ Cards display in grid
- ✅ Cards flip on click
- ✅ Two cards show emoji
- ✅ Matching cards stay flipped
- ✅ Non-matching cards flip back
- ✅ Move counter increments
- ✅ Matched counter shows correct count
- ✅ Win message appears
- ✅ Difficulty buttons work
- ✅ Grid changes size per difficulty
- ✅ Best score saves (localStorage)
- ✅ Mobile responsive (try on phone!)
Extensions
If you want more:
- Sound effects
// When cards match
new Audio('success.mp3').play();
- Time limit
let timeLeft = 120;
setInterval(() => { timeLeft--; }, 1000);
- Leaderboard
# Store scores in database
scores = [
{'name': 'Alex', 'moves': 15},
{'name': 'Jordan', 'moves': 18}
]
- Multiplayer
// Two players take turns
let currentPlayer = 1;
- More themes
const themes = {
animals: ['🐶', '🐱', '🐭', ...],
sports: ['⚽', '🏀', '🎾', ...],
weather: ['☀️', '⛅', '🌧️', ...]
};
Concepts You're Learning
Beginner:
- HTML structure & forms
- CSS styling & animations
- JavaScript basics (variables, functions)
- Event listeners (onclick)
Intermediate:
- DOM manipulation (create, modify, style elements)
- Game state management
- Array methods (push, includes, map, filter)
- Conditional logic (if/else)
- setTimeout (asynchronous code)
Advanced:
- Algorithm design (shuffle, matching)
- Performance (game loop)
- Data persistence (localStorage)
- Responsive design
- Web server (Flask)
Deploy to Web (Optional Friday Bonus)
This is the power of CoderKit: You built a web game on your phone. Now deploy it to the world.
Once you build it in CoderKit, you can deploy it free:
-
Option 1: Vercel (HTML/CSS/JS only)
- Push to GitHub
- Connect to Vercel
- Live at
yourname.vercel.app
-
Option 2: Railway (with Flask)
- Push to GitHub
- Connect to Railway
- Live immediately
-
Option 3: Heroku (Flask + Python)
- Same process
- Free tier available
Your CoderKit project becomes a live web game 🌐
The Real Power
You're not just "learning to code."
You're building production web applications on your phone.
The same HTML/CSS/JavaScript works:
- In CoderKit on your phone
- On your desktop browser
- On a live website
- On tablets and laptops
This scales. From this Memory Game to real SaaS products.
Your Achievement
You just built—on your phone with CoderKit:
- ✅ A web server (Flask)
- ✅ A responsive frontend (HTML/CSS)
- ✅ Complex game logic (JavaScript)
- ✅ Data persistence (localStorage)
- ✅ Mobile-friendly experience
- ✅ Production-ready code
This is real web development.
Not a tutorial project. Not "learning". This is professional-level code you'll use in real products. And you did it all on your phone.
Next Steps
After this, you're ready for:
- Quiz app (store questions, track scores)
- To-do app (persist data, edit/delete)
- Chat app (Flask with WebSockets)
- Dashboard (display real data)
- Full app (deploy for real users)
You have the skills. Pick what excites you.
Ready?
You have everything you need.
- Download CoderKit
- Create these files
- Build the game
- Ship it.
This game will teach you more than 100 hours of tutorials.
Let's build. 🚀
Download CoderKit Free - Start Building →
Your web game is waiting. Build it this week—without a laptop.
Share your game: Built your Memory Game in CoderKit? Tag us @coderkit with your screenshots!
Next week: Deploy your projects. From phone to the world in minutes.