Skip to content

Commit 31f8bf3

Browse files
yoannmoinetclaude
andcommitted
feat(docs): game polish — mobile perf, memory cleanup, snake visuals, asteroid tilt
Performance: - Skip shadowBlur on mobile (isMobile detection) across all 5 games - Clear all game arrays in destroy() to prevent memory leaks Snake (fog-explorer): - Radial gradient head with eyes that follow heading direction - Connecting gradient lines between segments for a body feel - Pulsing glow trail behind body (desktop only) Asteroid Dodge: - Subtle canvas tilt (lerped) in direction of horizontal movement Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 451ce68 commit 31f8bf3

File tree

5 files changed

+169
-48
lines changed

5 files changed

+169
-48
lines changed

packages/docs/src/games/asteroid-dodge.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ export const createGame: CreateGame = (_container) => {
4141
},
4242

4343
create(): GameInstance {
44+
const isMobile = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
45+
4446
let canvas: HTMLCanvasElement;
4547
let ctx: CanvasRenderingContext2D;
4648
let animId: number | null = null;
@@ -74,6 +76,7 @@ export const createGame: CreateGame = (_container) => {
7476
let particles: Particle[] = [];
7577
let shakeTime = 0;
7678
let flashAlpha = 0;
79+
let tilt = 0;
7780

7881
function triggerImpact() {
7982
shakeTime = 12;
@@ -194,7 +197,7 @@ export const createGame: CreateGame = (_container) => {
194197
function drawShip() {
195198
const sy = canvas.height - shipBottomOffset;
196199
ctx.save();
197-
ctx.shadowBlur = 12;
200+
ctx.shadowBlur = isMobile ? 0 : 12;
198201
ctx.shadowColor = SHIP_COLOR;
199202
ctx.fillStyle = SHIP_COLOR;
200203
ctx.beginPath();
@@ -224,7 +227,7 @@ export const createGame: CreateGame = (_container) => {
224227
ctx.save();
225228
ctx.translate(a.x, a.y);
226229
ctx.rotate(a.rotation);
227-
ctx.shadowBlur = 10;
230+
ctx.shadowBlur = isMobile ? 0 : 10;
228231
ctx.shadowColor = a.color;
229232
ctx.fillStyle = a.color;
230233
ctx.globalAlpha = 0.6;
@@ -262,7 +265,7 @@ export const createGame: CreateGame = (_container) => {
262265
const centerY = canvas.height / 2;
263266

264267
// GAME OVER text
265-
ctx.shadowBlur = 20;
268+
ctx.shadowBlur = isMobile ? 0 : 20;
266269
ctx.shadowColor = '#e879f9';
267270
ctx.font = 'bold 28px JetBrains Mono, monospace';
268271
ctx.fillStyle = '#e879f9';
@@ -376,6 +379,10 @@ export const createGame: CreateGame = (_container) => {
376379

377380
update();
378381

382+
// Smooth tilt toward target
383+
const tiltTarget = vectorX * 0.03;
384+
tilt += (tiltTarget - tilt) * 0.1;
385+
379386
ctx.save();
380387
if (shakeTime > 0) {
381388
const intensity = shakeTime / 12;
@@ -386,6 +393,11 @@ export const createGame: CreateGame = (_container) => {
386393
shakeTime--;
387394
}
388395

396+
// Apply subtle canvas tilt based on horizontal movement
397+
ctx.translate(canvas.width / 2, canvas.height / 2);
398+
ctx.rotate(tilt);
399+
ctx.translate(-canvas.width / 2, -canvas.height / 2);
400+
389401
drawBackground();
390402
drawAsteroids();
391403
if (!exploding && !gameOver) {
@@ -394,7 +406,7 @@ export const createGame: CreateGame = (_container) => {
394406
for (const p of particles) {
395407
ctx.save();
396408
ctx.globalAlpha = p.life;
397-
ctx.shadowBlur = 8;
409+
ctx.shadowBlur = isMobile ? 0 : 8;
398410
ctx.shadowColor = p.color;
399411
ctx.fillStyle = p.color;
400412
ctx.beginPath();
@@ -483,6 +495,9 @@ export const createGame: CreateGame = (_container) => {
483495
cancelAnimationFrame(animId);
484496
animId = null;
485497
}
498+
asteroids.length = 0;
499+
particles.length = 0;
500+
bgLines.length = 0;
486501
},
487502
};
488503
},

packages/docs/src/games/dual-stick-arena.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ export const createGame: CreateGame = (_container) => {
6363
},
6464

6565
create(): GameInstance {
66+
const isMobile = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
67+
6668
let canvas: HTMLCanvasElement;
6769
let ctx: CanvasRenderingContext2D;
6870
let animId: number | null = null;
@@ -223,7 +225,7 @@ export const createGame: CreateGame = (_container) => {
223225

224226
function drawPlayer() {
225227
ctx.save();
226-
ctx.shadowBlur = 15;
228+
ctx.shadowBlur = isMobile ? 0 : 15;
227229
ctx.shadowColor = PLAYER_COLOR;
228230
ctx.fillStyle = PLAYER_COLOR;
229231
ctx.beginPath();
@@ -267,7 +269,7 @@ export const createGame: CreateGame = (_container) => {
267269

268270
ctx.save();
269271
ctx.globalAlpha = alpha;
270-
ctx.shadowBlur = 10;
272+
ctx.shadowBlur = isMobile ? 0 : 10;
271273
ctx.shadowColor = ENEMY_COLOR;
272274
ctx.fillStyle = ENEMY_COLOR;
273275
ctx.beginPath();
@@ -302,7 +304,7 @@ export const createGame: CreateGame = (_container) => {
302304

303305
// Draw projectile
304306
ctx.save();
305-
ctx.shadowBlur = 8;
307+
ctx.shadowBlur = isMobile ? 0 : 8;
306308
ctx.shadowColor = PROJECTILE_COLOR;
307309
ctx.fillStyle = PROJECTILE_COLOR;
308310
ctx.beginPath();
@@ -328,7 +330,7 @@ export const createGame: CreateGame = (_container) => {
328330
const centerX = canvas.width / 2;
329331
const centerY = canvas.height / 2;
330332

331-
ctx.shadowBlur = 20;
333+
ctx.shadowBlur = isMobile ? 0 : 20;
332334
ctx.shadowColor = ENEMY_COLOR;
333335
ctx.font = 'bold 28px JetBrains Mono, monospace';
334336
ctx.fillStyle = ENEMY_COLOR;
@@ -462,7 +464,7 @@ export const createGame: CreateGame = (_container) => {
462464
for (const p of particles) {
463465
ctx.save();
464466
ctx.globalAlpha = p.life;
465-
ctx.shadowBlur = 8;
467+
ctx.shadowBlur = isMobile ? 0 : 8;
466468
ctx.shadowColor = p.color;
467469
ctx.fillStyle = p.color;
468470
ctx.beginPath();
@@ -593,6 +595,9 @@ export const createGame: CreateGame = (_container) => {
593595
cancelAnimationFrame(animId);
594596
animId = null;
595597
}
598+
enemies.length = 0;
599+
projectiles.length = 0;
600+
particles.length = 0;
596601
},
597602
};
598603
},

packages/docs/src/games/endless-chase.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ export const createGame: CreateGame = (_container) => {
7272
},
7373

7474
create(): GameInstance {
75+
const isMobile = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
76+
7577
let canvas: HTMLCanvasElement;
7678
let ctx: CanvasRenderingContext2D;
7779
let animId: number | null = null;
@@ -211,7 +213,7 @@ export const createGame: CreateGame = (_container) => {
211213

212214
// Near stars get a subtle glow
213215
if (star.depth > 0.7) {
214-
ctx.shadowBlur = star.size * 3;
216+
ctx.shadowBlur = isMobile ? 0 : star.size * 3;
215217
ctx.shadowColor = star.color;
216218
}
217219

@@ -247,7 +249,7 @@ export const createGame: CreateGame = (_container) => {
247249

248250
// Simple filled circle with soft glow
249251
ctx.save();
250-
ctx.shadowBlur = 15;
252+
ctx.shadowBlur = isMobile ? 0 : 15;
251253
ctx.shadowColor = target.glowColor;
252254
ctx.globalAlpha = target.locked ? 0.2 : 0.5;
253255
ctx.fillStyle = target.color;
@@ -354,7 +356,7 @@ export const createGame: CreateGame = (_container) => {
354356
ctx.translate(edgeX, edgeY);
355357
ctx.rotate(angle);
356358

357-
ctx.shadowBlur = 6;
359+
ctx.shadowBlur = isMobile ? 0 : 6;
358360
ctx.shadowColor = target.color;
359361
ctx.fillStyle = target.color;
360362
ctx.globalAlpha = 0.5;
@@ -385,7 +387,7 @@ export const createGame: CreateGame = (_container) => {
385387

386388
ctx.save();
387389
// Simple glowing dot
388-
ctx.shadowBlur = 18;
390+
ctx.shadowBlur = isMobile ? 0 : 18;
389391
ctx.shadowColor = CROSSHAIR_COLOR;
390392
ctx.fillStyle = CROSSHAIR_COLOR;
391393
ctx.globalAlpha = 0.9;
@@ -515,7 +517,7 @@ export const createGame: CreateGame = (_container) => {
515517
for (const p of particles) {
516518
ctx.save();
517519
ctx.globalAlpha = p.life * 0.8;
518-
ctx.shadowBlur = 10;
520+
ctx.shadowBlur = isMobile ? 0 : 10;
519521
ctx.shadowColor = p.color;
520522
ctx.fillStyle = p.color;
521523
ctx.beginPath();
@@ -608,6 +610,9 @@ export const createGame: CreateGame = (_container) => {
608610
cancelAnimationFrame(animId);
609611
animId = null;
610612
}
613+
particles.length = 0;
614+
stars.length = 0;
615+
targets.length = 0;
611616
},
612617
};
613618
},

0 commit comments

Comments
 (0)