// snake.ts / ME Creative & Design
const GRID = 20;
type Point = { x: number; y: number };
let snake: Point[] = [{ x: 8, y: 10 }, { x: 7, y: 10 }, { x: 6, y: 10 }];
let dir: Point = { x: 1, y: 0 };
let food: Point = { x: 14, y: 10 };
let score = 0;
function placeFood() {
do {
food = { x: rand(GRID), y: rand(GRID) };
} while (snake.some((s) => s.x === food.x && s.y === food.y));
}
function tick(): boolean {
const head = { x: snake[0].x + dir.x, y: snake[0].y + dir.y };
const hitWall = head.x < 0 || head.y < 0 || head.x >= GRID || head.y >= GRID;
const hitSelf = snake.some((s) => s.x === head.x && s.y === head.y);
if (hitWall || hitSelf) return false;
snake.unshift(head);
if (head.x === food.x && head.y === food.y) {
score += 1;
placeFood();
} else {
snake.pop();
}
return true;
}
function steer(next: Point) {
if (next.x === -dir.x && next.y === -dir.y) return; // no U-turns
dir = next;
}
window.addEventListener("keydown", (e) => {
if (e.key === "ArrowUp") steer({ x: 0, y: -1 });
if (e.key === "ArrowDown") steer({ x: 0, y: 1 });
if (e.key === "ArrowLeft") steer({ x: -1, y: 0 });
if (e.key === "ArrowRight") steer({ x: 1, y: 0 });
});
function draw(ctx: CanvasRenderingContext2D, cell: number) {
ctx.fillStyle = "#171c1e";
ctx.fillRect(0, 0, GRID * cell, GRID * cell);
ctx.fillStyle = "#e6a57a";
ctx.fillRect(food.x * cell, food.y * cell, cell, cell);
ctx.fillStyle = "#f6f1e7";
for (const s of snake) ctx.fillRect(s.x * cell, s.y * cell, cell - 1, cell - 1);
}
function loop() {
if (!tick()) return gameOver(score);
draw(ctx, cell);
setTimeout(loop, Math.max(60, 120 - score * 3));
}
loop();
// snake.ts / ME Creative & Design
const GRID = 20;
type Point = { x: number; y: number };
let snake: Point[] = [{ x: 8, y: 10 }, { x: 7, y: 10 }, { x: 6, y: 10 }];
let dir: Point = { x: 1, y: 0 };
let food: Point = { x: 14, y: 10 };
let score = 0;
function placeFood() {
do {
food = { x: rand(GRID), y: rand(GRID) };
} while (snake.some((s) => s.x === food.x && s.y === food.y));
}
function tick(): boolean {
const head = { x: snake[0].x + dir.x, y: snake[0].y + dir.y };
const hitWall = head.x < 0 || head.y < 0 || head.x >= GRID || head.y >= GRID;
const hitSelf = snake.some((s) => s.x === head.x && s.y === head.y);
if (hitWall || hitSelf) return false;
snake.unshift(head);
if (head.x === food.x && head.y === food.y) {
score += 1;
placeFood();
} else {
snake.pop();
}
return true;
}
function steer(next: Point) {
if (next.x === -dir.x && next.y === -dir.y) return; // no U-turns
dir = next;
}
window.addEventListener("keydown", (e) => {
if (e.key === "ArrowUp") steer({ x: 0, y: -1 });
if (e.key === "ArrowDown") steer({ x: 0, y: 1 });
if (e.key === "ArrowLeft") steer({ x: -1, y: 0 });
if (e.key === "ArrowRight") steer({ x: 1, y: 0 });
});
function draw(ctx: CanvasRenderingContext2D, cell: number) {
ctx.fillStyle = "#171c1e";
ctx.fillRect(0, 0, GRID * cell, GRID * cell);
ctx.fillStyle = "#e6a57a";
ctx.fillRect(food.x * cell, food.y * cell, cell, cell);
ctx.fillStyle = "#f6f1e7";
for (const s of snake) ctx.fillRect(s.x * cell, s.y * cell, cell - 1, cell - 1);
}
function loop() {
if (!tick()) return gameOver(score);
draw(ctx, cell);
setTimeout(loop, Math.max(60, 120 - score * 3));
}
loop();
// snake.ts / ME Creative & Design
const GRID = 20;
type Point = { x: number; y: number };
let snake: Point[] = [{ x: 8, y: 10 }, { x: 7, y: 10 }, { x: 6, y: 10 }];
let dir: Point = { x: 1, y: 0 };
let food: Point = { x: 14, y: 10 };
let score = 0;
function placeFood() {
do {
food = { x: rand(GRID), y: rand(GRID) };
} while (snake.some((s) => s.x === food.x && s.y === food.y));
}
function tick(): boolean {
const head = { x: snake[0].x + dir.x, y: snake[0].y + dir.y };
const hitWall = head.x < 0 || head.y < 0 || head.x >= GRID || head.y >= GRID;
const hitSelf = snake.some((s) => s.x === head.x && s.y === head.y);
if (hitWall || hitSelf) return false;
snake.unshift(head);
if (head.x === food.x && head.y === food.y) {
score += 1;
placeFood();
} else {
snake.pop();
}
return true;
}
function steer(next: Point) {
if (next.x === -dir.x && next.y === -dir.y) return; // no U-turns
dir = next;
}
window.addEventListener("keydown", (e) => {
if (e.key === "ArrowUp") steer({ x: 0, y: -1 });
if (e.key === "ArrowDown") steer({ x: 0, y: 1 });
if (e.key === "ArrowLeft") steer({ x: -1, y: 0 });
if (e.key === "ArrowRight") steer({ x: 1, y: 0 });
});
function draw(ctx: CanvasRenderingContext2D, cell: number) {
ctx.fillStyle = "#171c1e";
ctx.fillRect(0, 0, GRID * cell, GRID * cell);
ctx.fillStyle = "#e6a57a";
ctx.fillRect(food.x * cell, food.y * cell, cell, cell);
ctx.fillStyle = "#f6f1e7";
for (const s of snake) ctx.fillRect(s.x * cell, s.y * cell, cell - 1, cell - 1);
}
function loop() {
if (!tick()) return gameOver(score);
draw(ctx, cell);
setTimeout(loop, Math.max(60, 120 - score * 3));
}
loop();
Every site here starts as a conversation and ends as code. Design, engineering, and SEO come from one person, so nothing gets lost between a designer and a developer. Sites, stores, logins, galleries, and the admin behind them, built to fit the business, found on Google, and looked after once they are live.
What sets apart
What makes it different:
- One person, every step
- Designed with you
- Your voice, not a template
- SEO in every build
- Google scores at launch
- Care after launch
Built with:
- Next.js
- React
- TypeScript
- Supabase
- Stripe
- Resend
- Vercel