@@ -272,9 +272,19 @@ function gameEngine() {
272272 ctx . fillRect ( food . x * 20 , food . y * 20 , 18 , 18 ) ;
273273}
274274
275- function gameLoop ( ) {
276- gameEngine ( ) ;
275+ // rAF fires ~60 times/sec regardless of difficulty. To make `speed` actually
276+ // control how fast the snake moves, we throttle how often gameEngine() runs
277+ // based on elapsed time, using the high-res timestamp rAF provides.
278+ function gameLoop ( ctime ) {
277279 animationId = requestAnimationFrame ( gameLoop ) ;
280+
281+ if ( ! gameRunning || isPaused ) return ;
282+
283+ // Only advance the game `speed` times per second
284+ if ( ( ctime - lastPaintTime ) / 1000 < 1 / speed ) return ;
285+
286+ lastPaintTime = ctime ;
287+ gameEngine ( ) ;
278288}
279289
280290function restartGame ( ) {
@@ -285,6 +295,7 @@ function restartGame() {
285295 isPaused = false ;
286296 isGameOver = false ;
287297 gameRunning = true ;
298+ lastPaintTime = 0 ;
288299
289300 // Reset UI
290301 document . getElementById ( 'score' ) . textContent = '0' ;
@@ -311,6 +322,9 @@ function togglePause() {
311322 pauseOverlay . classList . remove ( 'hidden' ) ;
312323 if ( pauseBtn ) pauseBtn . textContent = 'Resume' ;
313324 } else {
325+ // Reset the throttle clock so the snake doesn't "catch up" with a
326+ // burst of queued moves after being paused.
327+ lastPaintTime = 0 ;
314328 pauseOverlay . classList . add ( 'hidden' ) ;
315329 if ( pauseBtn ) pauseBtn . textContent = 'Pause' ;
316330 }
@@ -334,6 +348,7 @@ function initSnakeGame() {
334348 direction = { x : 1 , y : 0 } ;
335349 snakeArr = [ { x : 13 , y : 10 } ] ;
336350 score = 0 ;
351+ lastPaintTime = 0 ;
337352
338353 // Update UI
339354 const scoreEl = document . getElementById ( 'score' ) ;
0 commit comments