Thanksgiving morning is race time, and this turkey 🦃 has entered the Turkey Trot! Tap the field, click, or press Space or the up arrow to jump over the pumpkins, hay bales and logs in its way. Every stretch of field you cover adds to your score, and each ear of corn 🌽 you grab in mid-air is worth five more points. The turkey runs faster and faster, and a single trip ends the race.
How far can your turkey trot before the feast?
.trot-game {
text-align: center;
}
.trot-game .instructions {
max-width: 500px;
margin: 0 auto 15px;
font-size: 1em;
color: #555;
}
.trot-info {
margin-bottom: 10px;
font-size: 1.2em;
}
#trotCanvas {
display: block;
margin: 0 auto;
width: 360px;
max-width: 100%;
height: auto;
border: 3px solid #8d4a1f;
border-radius: 10px;
background: #fdebd0;
touch-action: manipulation;
cursor: pointer;
}
.trot-message {
margin-top: 15px;
font-weight: bold;
}
.trot-game .start-btn {
margin-top: 15px;
padding: 10px 20px;
font-size: 1em;
background-color: #c0581f;
color: #fff;
font-weight: bold;
border: none;
border-radius: 5px;
cursor: pointer;
}
.trot-game .start-btn:hover {
background-color: #d9732f;
}
.trot-game .start-btn:disabled {
background-color: #ccc;
cursor: default;
}
<div class="trot-game">
<h1>🦃 Turkey Trot 🏃</h1>
<div class="instructions">
Tap the field, click, or press Space or the up arrow to jump. Leap over the pumpkins and hay bales, grab the corn, and run as far as you can!
</div>
<div class="trot-info">Score: <span id="trotScore">0</span> | Best: <span id="trotBest">0</span></div>
<canvas id="trotCanvas" width="360" height="240"></canvas>
<div class="trot-message" id="trotMessage">Press "Start", then tap to make the turkey jump!</div>
<button class="start-btn" id="trotStart">Start</button>
</div>
const canvas = document.getElementById('trotCanvas');
const ctx = canvas.getContext('2d');
const scoreDisplay = document.getElementById('trotScore');
const bestDisplay = document.getElementById('trotBest');
const message = document.getElementById('trotMessage');
const startButton = document.getElementById('trotStart');
const hiddenScore = document.getElementById('hiddenScore');
// The share buttons are rendered after this script, so look them up when needed.
function setShareButtons(display) {
const shareButtons = document.querySelector('.share-buttons');
if (shareButtons) {
shareButtons.style.display = display;
}
}
const W = canvas.width;
const H = canvas.height;
const GROUND = H - 40;
const PLAYER_X = 60;
const PLAYER_SIZE = 34;
const GRAVITY = 1500;
const JUMP_SPEED = 560;
const OBSTACLES = ['🎃', '🌾', '🪵'];
let bonus, playerY, velocity, onGround, items, score, distance, speed, running, waiting, lastTime, animationId, spawnTimer, best, leaves;
best = 0;
try {
best = parseInt(localStorage.getItem('turkeyTrotBest'), 10) || 0;
} catch (e) {
best = 0;
}
bestDisplay.textContent = best;
leaves = [];
for (let i = 0; i < 14; i++) {
leaves.push({ x: Math.random() * W, y: Math.random() * (GROUND - 40), s: 15 + Math.random() * 25 });
}
function setScore(value) {
score = value;
scoreDisplay.textContent = score;
if (hiddenScore) {
hiddenScore.value = score;
}
}
function reset() {
playerY = GROUND - PLAYER_SIZE;
velocity = 0;
onGround = true;
items = [];
distance = 0;
bonus = 0;
speed = 180;
spawnTimer = 1.2;
setScore(0);
}
function jump() {
if (!running) {
return;
}
if (waiting) {
waiting = false;
message.textContent = 'Run, turkey, run! 🦃';
lastTime = performance.now();
}
if (onGround) {
velocity = -JUMP_SPEED;
onGround = false;
}
}
function spawn() {
if (Math.random() < 0.3) {
items.push({ x: W + 20, y: GROUND - 95 - Math.random() * 30, kind: 'corn', emoji: '🌽', size: 26 });
} else {
items.push({ x: W + 20, y: GROUND - 30, kind: 'obstacle', emoji: OBSTACLES[Math.floor(Math.random() * OBSTACLES.length)], size: 30 });
}
}
function overlaps(item) {
const px = PLAYER_X + 6;
const py = playerY + 6;
const ps = PLAYER_SIZE - 12;
const m = 5;
return px < item.x + item.size - m && px + ps > item.x + m && py < item.y + item.size - m && py + ps > item.y + m;
}
function update(dt) {
speed += dt * 6;
distance += speed * dt;
velocity += GRAVITY * dt;
playerY += velocity * dt;
if (playerY >= GROUND - PLAYER_SIZE) {
playerY = GROUND - PLAYER_SIZE;
velocity = 0;
onGround = true;
}
spawnTimer -= dt;
if (spawnTimer <= 0) {
spawn();
const minGap = Math.max(0.55, 1.1 - speed / 1000);
spawnTimer = minGap + Math.random() * 0.8;
}
for (let i = items.length - 1; i >= 0; i--) {
const item = items[i];
item.x -= speed * dt;
if (item.x < -40) {
items.splice(i, 1);
continue;
}
if (overlaps(item)) {
if (item.kind === 'corn') {
items.splice(i, 1);
bonus += 5;
} else {
endGame();
return;
}
}
}
const total = Math.floor(distance / 50) + bonus;
if (total !== score) {
setScore(total);
}
leaves.forEach(function (l) {
l.x -= speed * dt * 0.3;
if (l.x < -20) {
l.x = W + 20;
l.y = Math.random() * (GROUND - 40);
}
});
}
function draw() {
ctx.clearRect(0, 0, W, H);
ctx.fillStyle = '#fdebd0';
ctx.fillRect(0, 0, W, H);
ctx.font = '14px serif';
ctx.globalAlpha = 0.5;
leaves.forEach(function (l) {
ctx.fillText('🍂', l.x, l.y);
});
ctx.globalAlpha = 1;
ctx.fillStyle = '#8bab4a';
ctx.fillRect(0, GROUND, W, H - GROUND);
ctx.fillStyle = '#6e8a37';
ctx.fillRect(0, GROUND, W, 4);
ctx.textBaseline = 'top';
items.forEach(function (item) {
ctx.font = item.size + 'px serif';
ctx.fillText(item.emoji, item.x, item.y);
});
ctx.save();
ctx.translate(PLAYER_X + PLAYER_SIZE, playerY);
ctx.scale(-1, 1);
ctx.font = PLAYER_SIZE + 'px serif';
ctx.fillText('🦃', 0, 0);
ctx.restore();
}
function loop(time) {
if (!running) {
return;
}
if (!waiting) {
const dt = Math.min(0.05, (time - lastTime) / 1000);
lastTime = time;
update(dt);
}
draw();
if (running) {
animationId = requestAnimationFrame(loop);
}
}
function startGame() {
reset();
running = true;
waiting = true;
startButton.disabled = true;
message.textContent = 'Tap the field or press Space to start running!';
setShareButtons('none');
lastTime = performance.now();
animationId = requestAnimationFrame(loop);
}
function endGame() {
running = false;
cancelAnimationFrame(animationId);
draw();
if (score > best) {
best = score;
bestDisplay.textContent = best;
try {
localStorage.setItem('turkeyTrotBest', String(best));
} catch (e) {
// ignore storage errors
}
message.textContent = 'Tripped! New best score: ' + score + '! 🏆';
} else {
message.textContent = 'Tripped! You scored ' + score + '. 🦃';
}
startButton.disabled = false;
startButton.textContent = 'Play again';
setShareButtons('flex');
}
canvas.addEventListener('touchstart', function (e) {
e.preventDefault();
jump();
}, { passive: false });
canvas.addEventListener('mousedown', function () {
jump();
});
document.addEventListener('keydown', function (e) {
if (!running) {
return;
}
if (e.code === 'Space' || e.code === 'ArrowUp') {
e.preventDefault();
jump();
}
});
startButton.addEventListener('click', startGame);
reset();
draw();