Want to make your Shopify store more interactive? You can embed the classic Snake Game directly into your site—no app required! By using custom HTML and JavaScript, you can engage visitors, increase time spent on your site, and add a fun, retro vibe that sets your store apart.
Create a new section named Snake-Game.liquid using the following code:
{% schema %}
{
"name": "Snake Game",
"settings": [
{
"type": "header",
"content": "Game Settings"
},
{
"type": "text",
"id": "game_title",
"label": "Game Title",
"default": "🎮 Snake Game – Tap Play to Start"
},
{
"type": "checkbox",
"id": "show_player_name",
"label": "Show Player Name Input",
"default": true,
"info": "Allow players to enter their name before starting"
},
{
"type": "text",
"id": "name_placeholder",
"label": "Name Input Placeholder",
"default": "Enter your name"
},
{
"type": "header",
"content": "Game Logic"
},
{
"type": "text",
"id": "big_food_score",
"label": "Big Food Score Value",
"default": "20"
},
{
"type": "header",
"content": "Colors"
},
{
"type": "color",
"id": "primary_color",
"label": "Primary Color (Snake, Borders, Buttons)",
"default": "#FCA312"
},
{
"type": "color",
"id": "background_color",
"label": "Section Background Color",
"default": "#0a0a0a"
},
{
"type": "color",
"id": "canvas_background",
"label": "Game Canvas Background",
"default": "#000000"
},
{
"type": "color",
"id": "food_color",
"label": "Food Color",
"default": "#ff0000"
},
{
"type": "color",
"id": "big_food_color",
"label": "Big Food Color",
"default": "#0000ff"
},
{
"type": "color",
"id": "snake_body_color",
"label": "Snake Body Color",
"default": "#ffffff"
},
{
"type": "header",
"content": "Button Text"
},
{
"type": "text",
"id": "play_button_text",
"label": "Play Button Text",
"default": "▶️ Play Game"
},
{
"type": "text",
"id": "play_again_text",
"label": "Play Again Button Text",
"default": "Play Again"
},
{
"type": "text",
"id": "cancel_button_text",
"label": "Cancel Button Text",
"default": "Cancel"
},
{
"type": "header",
"content": "Game Over Messages"
},
{
"type": "text",
"id": "game_over_title",
"label": "Game Over Title",
"default": "Game Over!"
},
{
"type": "text",
"id": "score_text",
"label": "Score Text",
"default": "Score:"
},
{
"type": "text",
"id": "final_score_text",
"label": "Final Score Text",
"default": "Your Score:"
}
],
"presets": [
{
"name": "Snake Game Section",
"category": "Custom"
}
]
}
{% endschema %}
<style>
/* Global game active state - prevent scrolling */
body.game-active {
overflow: hidden !important;
position: fixed !important;
width: 100% !important;
height: 100% !important;
}
#snake-container {
max-width: 100%;
padding: 30px 10px;
text-align: center;
background-color: {{ section.settings.background_color | default: '#0a0a0a' }};
color: {{ section.settings.primary_color | default: '#FCA312' }};
font-family: monospace;
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
min-height: 50vh;
}
/* Game mode container - fullscreen centered */
#snake-container.game-mode {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 9999;
padding: 20px;
justify-content: center;
background-color: {{ section.settings.background_color | default: '#0a0a0a' }};
overflow: hidden;
}
#snakeCanvas {
display: none;
border: 3px solid {{ section.settings.primary_color | default: '#FCA312' }};
background-color: {{ section.settings.canvas_background | default: '#000000' }};
margin: 0 auto;
outline: none;
max-width: 90vw;
max-height: 70vh;
}
/* Perfect canvas centering in game mode */
#snake-container.game-mode #snakeCanvas {
width: 400px;
height: 400px;
max-width: 90vw;
max-height: 60vh;
}
#score {
font-size: 20px;
margin: 10px 0;
display: none;
position: absolute;
top: 10px;
left: 50%;
transform: translateX(-50%);
z-index: 10;
}
/* Game mode score positioning */
#snake-container.game-mode #score {
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
font-size: 18px;
background-color: rgba(0, 0, 0, 0.7);
padding: 5px 15px;
border-radius: 5px;
border: 1px solid {{ section.settings.primary_color | default: '#FCA312' }};
}
.game-control {
padding: 10px 15px;
font-size: 16px;
border: 2px solid {{ section.settings.primary_color | default: '#FCA312' }};
border-radius: 8px;
background-color: {{ section.settings.canvas_background | default: '#000000' }};
color: {{ section.settings.primary_color | default: '#FCA312' }};
font-family: monospace;
text-align: center;
}
#start-controls {
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
margin-bottom: 15px;
width: 100%;
}
#player-name-input {
width: auto;
min-width: 250px;
}
select.game-control {
min-width: 250px;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23FCA312' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right 1rem center;
background-size: 1em;
padding-right: 2.5rem;
}
.game-control:focus {
outline: none;
box-shadow: 0 0 10px rgba(252, 163, 18, 0.5);
}
.play-btn {
padding: 12px 25px;
font-size: 18px;
background-color: {{ section.settings.primary_color | default: '#FCA312' }};
color: {{ section.settings.canvas_background | default: '#000000' }};
border: none;
border-radius: 8px;
cursor: pointer;
margin-bottom: 20px;
transition: transform 0.2s ease, background-color 0.2s ease;
z-index: 10;
display: block;
margin-left: auto;
margin-right: auto;
}
.game-over-btn {
padding: 12px 25px;
font-size: 18px;
background-color: {{ section.settings.primary_color | default: '#FCA312' }};
color: {{ section.settings.canvas_background | default: '#000000' }};
border: none;
border-radius: 8px;
cursor: pointer;
margin: 10px;
transition: transform 0.2s ease, background-color 0.2s ease;
display: inline-block;
}
.play-btn:hover, .game-over-btn:hover {
opacity: 0.8;
transform: scale(1.1);
}
.cancel-btn:hover {
background-color: #cc0000;
transform: scale(1.1);
}
.game-over-container {
display: none;
text-align: center;
padding: 20px;
background-color: {{ section.settings.canvas_background | default: '#000000' }};
border: 2px solid {{ section.settings.primary_color | default: '#FCA312' }};
border-radius: 10px;
margin: 20px 0;
box-shadow: 0 0 20px rgba(252, 163, 18, 0.3);
position: relative;
z-index: 20;
}
/* Game mode game over positioning */
#snake-container.game-mode .game-over-container {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
margin: 0;
max-width: 90vw;
max-height: 80vh;
overflow-y: auto;
}
.game-over-content {
color: {{ section.settings.primary_color | default: '#FCA312' }};
font-size: 24px;
}
.cancel-btn {
padding: 12px 25px;
font-size: 18px;
background-color: #ff0000;
color: #fff;
border: none;
border-radius: 8px;
cursor: pointer;
margin: 10px;
transition: transform 0.2s ease, background-color 0.2s ease;
display: inline-block;
}
/* Exit game button in game mode */
#exit-game-btn {
position: fixed;
top: 20px;
right: 20px;
z-index: 30;
padding: 8px 12px;
background-color: #ff0000;
color: #fff;
border: none;
border-radius: 50%;
cursor: pointer;
font-size: 16px;
width: 40px;
height: 40px;
display: none;
align-items: center;
justify-content: center;
transition: transform 0.2s ease;
}
#exit-game-btn:hover {
transform: scale(1.1);
background-color: #cc0000;
}
@media (max-width: 480px) {
#snakeCanvas {
width: 90vw !important;
height: 90vw !important;
max-height: 50vh !important;
}
#snake-container.game-mode #snakeCanvas {
width: 90vw !important;
height: 90vw !important;
max-height: 60vh !important;
}
.game-over-container {
margin: 10px 0;
padding: 15px;
}
.game-over-content {
font-size: 18px;
}
.game-over-btn, .cancel-btn {
display: block;
width: 100%;
margin: 10px 0;
}
.game-control {
min-width: 80%;
}
#score {
font-size: 16px;
}
#snake-container.game-mode #score {
font-size: 14px;
padding: 3px 10px;
}
}
</style>
<div id="snake-container">
<h2>{{ section.settings.game_title | default: '🎮 Snake Game – Tap Play to Start' }}</h2>
<div id="start-controls">
{% if section.settings.show_player_name %}
<input type="text" id="player-name-input" class="game-control" placeholder="{{ section.settings.name_placeholder | default: 'Enter your name' }}" maxlength="20">
{% endif %}
<select id="game-speed-selector" class="game-control">
<option value="180">Slow</option>
<option value="120" selected>Normal</option>
<option value="80">Fast</option>
</select>
</div>
<button class="play-btn" onclick="startGame()">{{ section.settings.play_button_text | default: '▶️ Play Game' }}</button>
<div id="score">{{ section.settings.score_text | default: 'Score:' }} 0</div>
<canvas id="snakeCanvas" width="400" height="400"></canvas>
<!-- Exit game button for fullscreen mode -->
<button id="exit-game-btn" onclick="cancelGame()" title="Exit Game">×</button>
<div id="game-over" class="game-over-container">
<div class="game-over-content">
<h3>{{ section.settings.game_over_title | default: 'Game Over!' }}</h3>
<p id="player-info"></p>
<p>{{ section.settings.final_score_text | default: 'Your Score:' }} <span id="final-score">0</span></p>
<button class="game-over-btn" onclick="startGame()">{{ section.settings.play_again_text | default: 'Play Again' }}</button>
<button class="cancel-btn" onclick="cancelGame()">{{ section.settings.cancel_button_text | default: 'Cancel' }}</button>
</div>
</div>
</div>
<script>
let canvas, ctx, box, snake, food, direction, gameInterval, score, playerName;
let eatSound, gameOverSound;
let gameActive = false;
let bigFood, foodEatenCount, isBigFoodOnScreen;
let bigFoodTimer, bigFoodInterval;
const showPlayerName = {{ section.settings.show_player_name | json }};
const bigFoodScore = parseInt("{{ section.settings.big_food_score | default: '20' }}");
function initSounds() {
eatSound = new Audio('{{ "beep-01a.mp3" | asset_url }}');
gameOverSound = new Audio('{{ "fail-buzzer-01.mp3" | asset_url }}');
}
function enterGameMode() {
// Enable fullscreen game mode
const container = document.getElementById('snake-container');
container.classList.add('game-mode');
document.body.classList.add('game-active');
document.getElementById('exit-game-btn').style.display = 'flex';
gameActive = true;
// Scroll to top and center
window.scrollTo(0, 0);
// Prevent body scroll
document.body.style.overflow = 'hidden';
document.body.style.position = 'fixed';
document.body.style.width = '100%';
document.body.style.height = '100%';
}
function exitGameMode() {
// Disable fullscreen game mode
const container = document.getElementById('snake-container');
container.classList.remove('game-mode');
document.body.classList.remove('game-active');
document.getElementById('exit-game-btn').style.display = 'none';
gameActive = false;
// Restore body scroll
document.body.style.overflow = '';
document.body.style.position = '';
document.body.style.width = '';
document.body.style.height = '';
}
function startGame() {
const gameSpeedDelay = parseInt(document.getElementById('game-speed-selector').value);
if (showPlayerName) {
const nameInput = document.getElementById('player-name-input');
playerName = nameInput.value.trim() || 'Anonymous';
}
// Enter fullscreen game mode
enterGameMode();
document.getElementById('start-controls').style.display = 'none';
document.querySelector('.play-btn').style.display = 'none';
document.getElementById('game-over').style.display = 'none';
document.getElementById('snakeCanvas').style.display = 'block';
document.getElementById('score').style.display = 'block';
canvas = document.getElementById("snakeCanvas");
ctx = canvas.getContext("2d");
box = 20;
score = 0;
direction = null;
snake = [{ x: 9 * box, y: 10 * box }];
foodEatenCount = 0;
isBigFoodOnScreen = false;
bigFood = {};
clearInterval(bigFoodInterval);
food = spawnFood();
updateScoreDisplay();
initSounds();
// Remove existing event listeners
document.removeEventListener("keydown", setDirection);
canvas.removeEventListener("touchstart", startTouch);
canvas.removeEventListener("touchmove", moveTouch);
// Add new event listeners
document.addEventListener("keydown", setDirection);
canvas.addEventListener("touchstart", startTouch, { passive: false });
canvas.addEventListener("touchmove", moveTouch, { passive: false });
// Focus canvas for keyboard input
canvas.focus();
canvas.tabIndex = 1;
ctx.clearRect(0, 0, canvas.width, canvas.height);
clearInterval(gameInterval);
gameInterval = setInterval(draw, gameSpeedDelay);
}
function updateScoreDisplay() {
const scoreText = "{{ section.settings.score_text | default: 'Score:' }}";
let displayText = scoreText + " " + score;
if (showPlayerName && playerName) {
displayText = playerName + " - " + displayText;
}
document.getElementById("score").textContent = displayText;
}
function setDirection(e) {
// Only handle arrow keys when game is active
if (!gameActive) return;
// Prevent default behavior for arrow keys and space
if ([37, 38, 39, 40, 32].includes(e.keyCode)) {
e.preventDefault();
e.stopPropagation();
}
// ESC key to exit game
if (e.keyCode === 27) {
cancelGame();
return;
}
if (e.keyCode === 37 && direction !== "RIGHT") direction = "LEFT";
if (e.keyCode === 38 && direction !== "DOWN") direction = "UP";
if (e.keyCode === 39 && direction !== "LEFT") direction = "RIGHT";
if (e.keyCode === 40 && direction !== "UP") direction = "DOWN";
}
let xStart = null, yStart = null;
function startTouch(e) {
if (!gameActive) return;
e.preventDefault();
e.stopPropagation();
xStart = e.touches[0].clientX;
yStart = e.touches[0].clientY;
}
function moveTouch(e) {
if (!gameActive) return;
e.preventDefault();
e.stopPropagation();
if (!xStart || !yStart) return;
let xEnd = e.touches[0].clientX;
let yEnd = e.touches[0].clientY;
let xDiff = xStart - xEnd;
let yDiff = yStart - yEnd;
if (Math.abs(xDiff) > Math.abs(yDiff)) {
if (xDiff > 0 && direction !== "RIGHT") direction = "LEFT";
else if (xDiff < 0 && direction !== "LEFT") direction = "RIGHT";
} else {
if (yDiff > 0 && direction !== "DOWN") direction = "UP";
else if (yDiff < 0 && direction !== "UP") direction = "DOWN";
}
xStart = null;
yStart = null;
}
function spawnFood() {
return {
x: Math.floor(Math.random() * 19 + 1) * box,
y: Math.floor(Math.random() * 19 + 1) * box
};
}
function cancelGame() {
// Exit fullscreen game mode
exitGameMode();
document.getElementById('game-over').style.display = 'none';
document.getElementById('snakeCanvas').style.display = 'none';
document.getElementById('score').style.display = 'none';
document.getElementById('start-controls').style.display = 'flex';
document.querySelector('.play-btn').style.display = 'block';
if (showPlayerName) {
document.getElementById('player-name-input').value = '';
}
document.getElementById('game-speed-selector').value = '120';
clearInterval(gameInterval);
clearInterval(bigFoodInterval);
if(canvas) {
document.removeEventListener("keydown", setDirection);
canvas.removeEventListener("touchstart", startTouch);
canvas.removeEventListener("touchmove", moveTouch);
}
}
function draw() {
ctx.fillStyle = "{{ section.settings.canvas_background | default: '#000000' }}";
ctx.fillRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < snake.length; i++) {
ctx.fillStyle = i === 0 ? "{{ section.settings.primary_color | default: '#FCA312' }}" : "{{ section.settings.snake_body_color | default: '#ffffff' }}";
ctx.fillRect(snake[i].x, snake[i].y, box, box);
}
ctx.fillStyle = "{{ section.settings.food_color | default: '#ff0000' }}";
ctx.beginPath();
ctx.arc(food.x + box / 2, food.y + box / 2, box / 2 - 2, 0, 2 * Math.PI);
ctx.fill();
if(isBigFoodOnScreen) {
ctx.fillStyle = "{{ section.settings.big_food_color | default: '#0000ff' }}";
ctx.beginPath();
ctx.arc(bigFood.x + box / 2, bigFood.y + box / 2, box / 2, 0, 2 * Math.PI);
ctx.fill();
ctx.fillStyle = "#FFFFFF";
ctx.font = "16px monospace";
ctx.textAlign = "center";
ctx.fillText(bigFoodTimer, bigFood.x + box / 2, bigFood.y - 10);
}
let headX = snake[0].x;
let headY = snake[0].y;
if (direction) {
if (direction === "LEFT") headX -= box;
if (direction === "UP") headY -= box;
if (direction === "RIGHT") headX += box;
if (direction === "DOWN") headY += box;
}
if (
headX < 0 || headX >= canvas.width || headY < 0 || headY >= canvas.height ||
snake.some((seg, i) => i !== 0 && seg.x === headX && seg.y === headY)
) {
clearInterval(gameInterval);
clearInterval(bigFoodInterval);
if (gameOverSound) gameOverSound.play();
const playerInfo = document.getElementById('player-info');
if (showPlayerName && playerName) {
playerInfo.textContent = "Player: " + playerName;
playerInfo.style.display = 'block';
} else {
playerInfo.style.display = 'none';
}
document.getElementById('final-score').textContent = score;
document.getElementById('game-over').style.display = 'block';
document.getElementById('snakeCanvas').style.display = 'none';
document.getElementById('score').style.display = 'none';
document.removeEventListener("keydown", setDirection);
canvas.removeEventListener("touchstart", startTouch);
canvas.removeEventListener("touchmove", moveTouch);
return;
}
let ateSomething = false;
if (isBigFoodOnScreen && headX === bigFood.x && headY === bigFood.y) {
score += bigFoodScore;
isBigFoodOnScreen = false;
ateSomething = true;
if (eatSound) eatSound.play();
clearInterval(bigFoodInterval);
}
else if (headX === food.x && headY === food.y) {
score += 10;
foodEatenCount++;
food = spawnFood();
ateSomething = true;
if (eatSound) eatSound.play();
if (foodEatenCount % 5 === 0 && !isBigFoodOnScreen) {
bigFood = spawnFood();
isBigFoodOnScreen = true;
bigFoodTimer = 10;
clearInterval(bigFoodInterval);
bigFoodInterval = setInterval(() => {
bigFoodTimer--;
if (bigFoodTimer <= 0) {
isBigFoodOnScreen = false;
clearInterval(bigFoodInterval);
}
}, 1000);
}
}
if (ateSomething) {
updateScoreDisplay();
} else {
if (direction) snake.pop();
}
if (direction) {
snake.unshift({ x: headX, y: headY });
}
}
// Prevent right-click context menu on canvas
document.addEventListener('contextmenu', function(e) {
if (gameActive && e.target.tagName === 'CANVAS') {
e.preventDefault();
}
});
// Prevent zoom gestures on mobile
document.addEventListener('touchmove', function(e) {
if (gameActive && e.touches.length > 1) {
e.preventDefault();
}
}, { passive: false });
// Prevent pinch zoom
document.addEventListener('wheel', function(e) {
if (gameActive && e.ctrlKey) {
e.preventDefault();
}
}, { passive: false });
</script>
5/5 - (5 votes)