🎮ArcadeLab

Geometry Dash Style

by CrystalGlider19
610 lines13.0 KB
▶ Play
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Geometry Dash Style</title>
<style>
    * { box-sizing: border-box; }

    body {
        margin: 0;
        background: #090b18;
        overflow: hidden;
        font-family: Arial, sans-serif;
        user-select: none;
    }

    canvas {
        display: block;
        width: 100vw;
        height: 100vh;
    }

    #ui {
        position: fixed;
        top: 18px;
        left: 50%;
        transform: translateX(-50%);
        color: white;
        font-weight: bold;
        text-align: center;
        pointer-events: none;
        text-shadow: 0 2px 5px #000;
    }

    #title {
        font-size: 26px;
        letter-spacing: 3px;
    }

    #hint {
        font-size: 14px;
        opacity: .7;
        margin-top: 6px;
    }

    #dead {
        display: none;
        position: fixed;
        inset: 0;
        align-items: center;
        justify-content: center;
        flex-direction: column;
        background: rgba(0,0,0,.55);
        color: white;
        text-align: center;
    }

    #dead h1 {
        font-size: 64px;
        margin: 0 0 10px;
        color: #ff3d71;
    }

    #dead p {
        font-size: 20px;
    }
</style>
</head>
<body>

<canvas id="game"></canvas>

<div id="ui">
    <div id="title">GEOMETRY RUN</div>
    <div id="hint">SPACE / CLICK TO JUMP</div>
</div>

<div id="dead">
    <h1>CRASHED!</h1>
    <p>Press SPACE or R to try again</p>
</div>

<script>
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");

let W, H;
function resize() {
    W = canvas.width = innerWidth * devicePixelRatio;
    H = canvas.height = innerHeight * devicePixelRatio;
    canvas.style.width = innerWidth + "px";
    canvas.style.height = innerHeight + "px";
}
resize();
addEventListener("resize", resize);

const dpr = () => devicePixelRatio;

let player;
let obstacles;
let particles;
let camera;
let speed;
let gameOver;
let won;
let distance;

const groundHeight = 100;

function reset() {
    player = {
        x: 180,
        y: 0,
        size: 42,
        vy: 0,
        rotation: 0,
        grounded: false
    };

    obstacles = [];
    particles = [];
    camera = 0;
    speed = 8;
    gameOver = false;
    won = false;
    distance = 0;

    document.getElementById("dead").style.display = "none";

    generateLevel();
}

function generateLevel() {
    obstacles = [];

    // Ground spikes and platforms
    const pattern = [
        ["spike", 650],
        ["spike", 850],
        ["spike", 1050],
        ["double", 1300],
        ["spike", 1600],
        ["block", 1850],
        ["spike", 2200],
        ["double", 2450],
        ["triple", 2800],
        ["block", 3300],
        ["spike", 3650],
        ["double", 3900],
        ["spike", 4250],
        ["triple", 4500],
        ["block", 5000],
        ["double", 5350],
        ["spike", 5750],
        ["triple", 6000],
        ["block", 6500],
        ["double", 7000],
        ["spike", 7500],
        ["triple", 7800],
        ["block", 8300],
        ["double", 8750],
        ["spike", 9200],
        ["triple", 9500],
        ["spike", 10000]
    ];

    for (const [type, x] of pattern) {
        if (type === "spike") {
            obstacles.push({
                type: "spike",
                x,
                y: 0,
                w: 48,
                h: 48
            });
        }

        if (type === "double") {
            obstacles.push({
                type: "spike",
                x,
                y: 0,
                w: 48,
                h: 48
            });
            obstacles.push({
                type: "spike",
                x: x + 45,
                y: 0,
                w: 48,
                h: 48
            });
        }

        if (type === "triple") {
            for (let i = 0; i < 3; i++) {
                obstacles.push({
                    type: "spike",
                    x: x + i * 45,
                    y: 0,
                    w: 48,
                    h: 48
                });
            }
        }

        if (type === "block") {
            obstacles.push({
                type: "block",
                x,
                y: 0,
                w: 60,
                h: 80
            });
        }
    }
}

function jump() {
    if (gameOver) {
        reset();
        return;
    }

    if (player.grounded) {
        player.vy = -17;
        player.grounded = false;

        for (let i = 0; i < 8; i++) {
            particles.push({
                x: player.x + player.size / 2,
                y: player.y + player.size,
                vx: (Math.random() - .5) * 5,
                vy: Math.random() * 3,
                life: 25
            });
        }
    }
}

addEventListener("keydown", e => {
    if (e.code === "Space") {
        e.preventDefault();
        jump();
    }

    if (e.key.toLowerCase() === "r") {
        reset();
    }
});

addEventListener("mousedown", jump);
addEventListener("touchstart", e => {
    e.preventDefault();
    jump();
}, { passive: false });

function collide(a, b) {
    return (
        a.x < b.x + b.w &&
        a.x + a.size > b.x &&
        a.y < b.y + b.h &&
        a.y + a.size > b.y
    );
}

function die() {
    if (gameOver) return;

    gameOver = true;

    for (let i = 0; i < 35; i++) {
        particles.push({
            x: player.x + player.size / 2,
            y: player.y + player.size / 2,
            vx: (Math.random() - .5) * 12,
            vy: (Math.random() - .5) * 12,
            life: 50
        });
    }

    document.getElementById("dead").style.display = "flex";
}

function update() {
    if (gameOver) {
        updateParticles();
        return;
    }

    distance += speed;
    camera += speed;

    // Slight difficulty increase
    speed = 8 + Math.min(distance / 3500, 3);

    player.vy += .85;
    player.y += player.vy;

    const groundY = innerHeight - groundHeight;

    if (player.y + player.size >= groundY) {
        player.y = groundY - player.size;
        player.vy = 0;
        player.grounded = true;
    } else {
        player.grounded = false;
    }

    if (!player.grounded) {
        player.rotation += .13;
    } else {
        player.rotation = Math.round(player.rotation / (Math.PI / 2))
                         * (Math.PI / 2);
    }

    for (const o of obstacles) {
        const ox = o.x - camera + player.x;

        if (o.type === "spike") {
            // More accurate triangle collision
            const box = {
                x: ox + 8,
                y: groundY - o.h + 10,
                w: o.w - 16,
                h: o.h - 10
            };

            if (collide(player, box)) {
                die();
            }
        }

        if (o.type === "block") {
            const box = {
                x: ox,
                y: groundY - o.h,
                w: o.w,
                h: o.h
            };

            if (collide(player, box)) {
                // Allow landing on top
                if (
                    player.vy >= 0 &&
                    player.y + player.size - player.vy <= box.y + 10
                ) {
                    player.y = box.y - player.size;
                    player.vy = 0;
                    player.grounded = true;
                } else {
                    die();
                }
            }
        }
    }

    if (distance > 10500) {
        won = true;
        gameOver = true;
        document.getElementById("dead").innerHTML =
            "<h1 style='color:#5dffce'>LEVEL COMPLETE!</h1>" +
            "<p>Press SPACE to play again</p>";
        document.getElementById("dead").style.display = "flex";
    }

    updateParticles();
}

function updateParticles() {
    for (const p of particles) {
        p.x += p.vx;
        p.y += p.vy;
        p.vy += .25;
        p.life--;
    }

    particles = particles.filter(p => p.life > 0);
}

function drawBackground() {
    // Gradient
    const gradient = ctx.createLinearGradient(0, 0, 0, H);
    gradient.addColorStop(0, "#151a46");
    gradient.addColorStop(1, "#080914");

    ctx.fillStyle = gradient;
    ctx.fillRect(0, 0, W, H);

    // Grid
    ctx.strokeStyle = "rgba(100,150,255,.08)";
    ctx.lineWidth = 2 * dpr();

    const grid = 80 * dpr();

    for (let x = -camera % grid; x < W; x += grid) {
        ctx.beginPath();
        ctx.moveTo(x, 0);
        ctx.lineTo(x, H);
        ctx.stroke();
    }

    for (let y = 0; y < H; y += grid) {
        ctx.beginPath();
        ctx.moveTo(0, y);
        ctx.lineTo(W, y);
        ctx.stroke();
    }
}

function drawGround() {
    const y = H - groundHeight * dpr();

    ctx.fillStyle = "#171b35";
    ctx.fillRect(0, y, W, groundHeight * dpr());

    ctx.fillStyle = "#36e8ff";
    ctx.fillRect(0, y, W, 5 * dpr());

    // Ground decoration
    ctx.strokeStyle = "rgba(54,232,255,.18)";
    ctx.lineWidth = 2 * dpr();

    const spacing = 55 * dpr();
    for (let x = -camera % spacing; x < W; x += spacing) {
        ctx.beginPath();
        ctx.moveTo(x, y + 5 * dpr());
        ctx.lineTo(x + 25 * dpr(), y + 35 * dpr());
        ctx.stroke();
    }
}

function drawObstacle(o) {
    const x = (o.x - camera + player.x) * dpr();
    const groundY = H - groundHeight * dpr();

    if (x < -100 || x > W + 100) return;

    if (o.type === "spike") {
        ctx.beginPath();
        ctx.moveTo(x, groundY);
        ctx.lineTo(x + o.w * dpr() / 2, groundY - o.h * dpr());
        ctx.lineTo(x + o.w * dpr(), groundY);
        ctx.closePath();

        ctx.fillStyle = "#ff3d71";
        ctx.fill();

        ctx.strokeStyle = "#fff";
        ctx.lineWidth = 2 * dpr();
        ctx.stroke();
    }

    if (o.type === "block") {
        ctx.fillStyle = "#7b4dff";
        ctx.fillRect(
            x,
            groundY - o.h * dpr(),
            o.w * dpr(),
            o.h * dpr()
        );

        ctx.strokeStyle = "#d5c9ff";
        ctx.lineWidth = 3 * dpr();
        ctx.strokeRect(
            x,
            groundY - o.h * dpr(),
            o.w * dpr(),
            o.h * dpr()
        );

        // Inner pattern
        ctx.strokeStyle = "rgba(255,255,255,.2)";
        ctx.lineWidth = 2 * dpr();

        for (let i = 0; i < o.w; i += 20) {
            ctx.beginPath();
            ctx.moveTo(x + i * dpr(), groundY);
            ctx.lineTo(x + (i + 30) * dpr(),
                       groundY - o.h * dpr());
            ctx.stroke();
        }
    }
}

function drawPlayer() {
    const x = player.x * dpr();
    const y = player.y * dpr();
    const s = player.size * dpr();

    ctx.save();

    ctx.translate(x + s / 2, y + s / 2);
    ctx.rotate(player.rotation);

    // Glow
    ctx.shadowBlur = 20 * dpr();
    ctx.shadowColor = "#fff43d";

    ctx.fillStyle = "#ffe83d";
    ctx.fillRect(-s / 2, -s / 2, s, s);

    ctx.shadowBlur = 0;

    // Face
    ctx.fillStyle = "#11152a";

    ctx.fillRect(
        -s * .25,
        -s * .18,
        s * .13,
        s * .13
    );

    ctx.fillRect(
        s * .12,
        -s * .18,
        s * .13,
        s * .13
    );

    ctx.fillRect(
        -s * .18,
        s * .13,
        s * .36,
        s * .08
    );

    ctx.strokeStyle = "white";
    ctx.lineWidth = 3 * dpr();
    ctx.strokeRect(-s / 2, -s / 2, s, s);

    ctx.restore();
}

function drawParticles() {
    for (const p of particles) {
        ctx.globalAlpha = p.life / 50;
        ctx.fillStyle = "#ffe83d";
        ctx.fillRect(
            p.x * dpr(),
            p.y * dpr(),
            7 * dpr(),
            7 * dpr()
        );
    }

    ctx.globalAlpha = 1;
}

function drawProgress() {
    const progress = Math.min(distance / 10500, 1);

    ctx.fillStyle = "rgba(255,255,255,.15)";
    ctx.fillRect(
        20 * dpr(),
        80 * dpr(),
        (innerWidth - 40) * dpr(),
        7 * dpr()
    );

    ctx.fillStyle = "#36e8ff";
    ctx.fillRect(
        20 * dpr(),
        80 * dpr(),
        (innerWidth - 40) * dpr() * progress,
        7 * dpr()
    );

    ctx.fillStyle = "white";
    ctx.font = `${14 * dpr()}px Arial`;
    ctx.textAlign = "right";
    ctx.fillText(
        Math.floor(progress * 100) + "%",
        (innerWidth - 20) * dpr(),
        75 * dpr()
    );
}

function draw() {
    ctx.clearRect(0, 0, W, H);

    drawBackground();
    drawGround();

    for (const o of obstacles) {
        drawObstacle(o);
    }

    drawPlayer();
    drawParticles();
    drawProgress();
}

function loop() {
    update();
    draw();
    requestAnimationFrame(loop);
}

reset();
loop();
</script>

</body>
</html>
```

Game Source: Geometry Dash Style

Creator: CrystalGlider19

Libraries: none

Complexity: complex (610 lines, 13.0 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: geometry-dash-style-crystalglider19" to link back to the original. Then publish at arcadelab.ai/publish.