Use P5 with canvas to create a Snake game
In the NPM Packages popup (bottom right) under the section: Add External Scripts, add P5:
name:p5
url:https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.2/p5.min.js
In this example, P5 is used in instance mode
//hide
const s = p => {
let gridWidth = 30, gridHeight = 30, gameStarted = false, startingSegments = 10;
let xStart = 5, yStart = 15, direction = 'right', segments = [], score = 0, fruit;
p.setup = function() {
p.createCanvas(500, 500);
p.frameRate(10);
p.textAlign(p.CENTER, p.CENTER);
p.textSize(1);
startGame();
}
p.draw = function() {
p.background(0);
p.scale(p.width / gridWidth, p.height / gridHeight);
gameStarted ? playGame() : showStartScreen();
}
function playGame() {
p.translate(0.5, 0.5);
showFruit();
showSnake();
updateSnake();
if (checkCollision()) gameOver();
else if (eatFruit()) growSnake();
}
function showStartScreen() {
p.fill(32);
p.rect(2, gridHeight / 2 - 5, gridWidth - 4, 10, 2);
p.fill(255);
p.text('Click to play', gridWidth / 2, gridHeight / 2);
p.noLoop();
}
p.mousePressed = function() {
gameStarted = true;
p.loop();
}
function showFruit() {
p.stroke(255, 64, 32);
p.point(fruit.x, fruit.y);
}
function showSnake() {
p.noFill();
p.stroke(96, 255, 64);
p.beginShape();
segments.forEach(seg => p.vertex(seg.x, seg.y));
p.endShape();
}
function updateSnake() {
segments.pop();
let head = segments[0].copy();
segments.unshift(moveHead(head));
}
function moveHead(head) {
if (direction === 'right') head.x++;
if (direction === 'left') head.x--;
if (direction === 'up') head.y--;
if (direction === 'down') head.y++;
return head;
}
function checkCollision() {
let head = segments[0];
return (
head.x < 0 || head.x >= gridWidth ||
head.y < 0 || head.y >= gridHeight ||
segments.slice(1).some(seg => seg.equals(head))
);
}
function gameOver() {
p.fill(32);
p.rect(2, gridHeight / 2 - 5, gridWidth - 4, 10, 2);
p.fill(255);
p.text(`Game over!\nScore: ${score}`, gridWidth / 2, gridHeight / 2);
gameStarted = false;
p.noLoop();
}
function eatFruit() {
return segments[0].equals(fruit);
}
function growSnake() {
score++;
segments.push(segments[segments.length - 1].copy());
updateFruit();
}
function updateFruit() {
do {
fruit = p.createVector(p.floor(p.random(gridWidth)), p.floor(p.random(gridHeight)));
} while (segments.some(seg => seg.equals(fruit))); // Prevent the fruit from appearing on the snake
}
p.keyPressed = function() {
if (p.keyCode === p.LEFT_ARROW && direction !== 'right') direction = 'left';
if (p.keyCode === p.RIGHT_ARROW && direction !== 'left') direction = 'right';
if (p.keyCode === p.UP_ARROW && direction !== 'down') direction = 'up';
if (p.keyCode === p.DOWN_ARROW && direction !== 'up') direction = 'down';
}
function startGame() {
segments = Array.from({ length: startingSegments }, (_, i) => p.createVector(xStart - i, yStart));
updateFruit();
score = 0;
direction = 'right';
gameStarted = false;
p.noLoop();
}
}
htmlEl.setAttribute('tabindex', '0'); // make it focusable
htmlEl.addEventListener('keydown', function(event) {
if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
event.preventDefault(); // Prevents scrolling behavior
}
});
htmlEl.focus();
htmlEl.innerHTML = ''; // Resets the HTML element before loading p5
const P5 = new p5(s, htmlEl);