Fish Survival is a game where you grow bigger by eating fish smaller than you. By building it, you'll pick up techniques no game developer can live without: canvas drawing basics, mouse following, and circle-to-circle collision detection.
First, set up the canvas. Grab the canvas element and prepare the drawing context (ctx). Use devicePixelRatio so the game also looks sharp on high-resolution displays.
var canvas = document.getElementById('game-canvas');
var ctx = canvas.getContext('2d');
function resizeCanvas() {
var wrapper = canvas.parentElement;
var rect = wrapper.getBoundingClientRect();
var dpr = window.devicePixelRatio || 1;
canvasW = rect.width;
canvasH = rect.height;
canvas.width = canvasW * dpr;
canvas.height = canvasH * dpr;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
}
getContext('2d') gets you a context for 2D drawing. Multiplying by devicePixelRatio keeps everything crisp even on Retina-style displays.
The player's fish is managed as an object. Its position (x, y) and size (radius) are the essential pieces of information.
var player = {
x: canvasW / 2,
y: canvasH / 2,
radius: 12,
color: '#3498DB',
vx: 0, vy: 0,
tailPhase: 0
};
We use canvasW / 2 and canvasH / 2 so the fish starts in the center of the screen. tailPhase is used to animate the tail fin.
Now let's make the player's fish follow the mouse. Read the mouse coordinates and nudge the player a little closer each frame for smooth movement.
var targetX, targetY;
var PLAYER_LERP = 0.08;
canvas.addEventListener('mousemove', function (e) {
var rect = canvas.getBoundingClientRect();
targetX = e.clientX - rect.left;
targetY = e.clientY - rect.top;
});
// Inside the update function
var dx = targetX - player.x;
var dy = targetY - player.y;
var d = Math.sqrt(dx * dx + dy * dy);
if (d > 1) {
var speed = Math.min(3.5 + player.radius * 0.02, d * PLAYER_LERP + 1.75);
player.vx = (dx / d) * speed;
player.vy = (dy / d) * speed;
player.x += player.vx;
player.y += player.vy;
}
Using PLAYER_LERP (linear interpolation) makes the fish glide gently toward the mouse instead of sticking right to it, which feels much more natural.
Make NPC fish appear at random from the edges of the screen. Balance the mix of small and large fish based on the player's size.
function createNPC() {
var radius = rand(5, 40);
var edge = Math.floor(Math.random() * 4);
var x, y;
if (edge === 0) { x = rand(radius, canvasW - radius); y = -radius * 3; }
else if (edge === 1) { x = rand(radius, canvasW - radius); y = canvasH + radius * 3; }
else if (edge === 2) { x = -radius * 3; y = rand(radius, canvasH - radius); }
else { x = canvasW + radius * 3; y = rand(radius, canvasH - radius); }
return {
x: x, y: y, radius: radius,
color: FISH_COLORS[Math.floor(Math.random() * FISH_COLORS.length)],
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed
};
}
We pick a random spawn point on one of the four edges (top, bottom, left, right) and send the fish swimming toward the center of the screen.
Circle-to-circle collision is just comparing "the distance between the two centers" with "the sum of the two radii." If the distance is smaller than the sum, the circles are touching.
function dist(x1, y1, x2, y2) {
var dx = x1 - x2;
var dy = y1 - y2;
return Math.sqrt(dx * dx + dy * dy);
}
// Collision check
var d = dist(player.x, player.y, npc.x, npc.y);
var touchDist = player.radius + npc.radius;
if (d < touchDist * 0.7) {
if (player.radius > npc.radius) {
// Eat a fish smaller than you
score += Math.ceil(npc.radius);
player.radius += npc.radius * 0.4 / player.radius * 5;
} else {
// Get eaten by a bigger fish -> game over
gameOver();
}
}
Allowing a little slack with touchDist * 0.7 makes the collision feel right visually. Touching a bigger fish means game over; touching a smaller one earns points and makes you grow.
Finally, build the game loop that repeats "update, then draw" every frame.
function gameLoop() {
update();
draw();
if (gameRunning) {
animFrameId = requestAnimationFrame(gameLoop);
}
}
requestAnimationFrame calls your function in step with the browser's drawing rhythm. It usually runs about 60 times a second, which gives you smooth animation.
Great job! Using four core techniques — canvas drawing, mouse following, circle collision detection, and a game loop — you now understand how Fish Survival works. To take it further, try adding background effects like bubbles and seaweed, more kinds of fish, or power-up items.
A: Yes, absolutely. This tutorial walks you through the code step by step, so even if it's your first time, you can finish it by following along in order.
A: You can build the basics in about 30 minutes to an hour. Polishing the visuals or adding features makes it even more fun to expand.