diff --git a/src/data/content.js b/src/data/content.js
index 1a4557e..17fe0e7 100644
--- a/src/data/content.js
+++ b/src/data/content.js
@@ -23,6 +23,7 @@ import flagger from "../assets/games/flag guess/flagger.png";
import Calculator from "../pages/activities/Calculator";
import { DogHttpCode } from "../pages/activities/DogHttpCode";
import { CatHttpCode } from "../pages/activities/CatHttpCode";
+import FlappyBird from "../pages/games/FlappyBird";
export const activities = [
{
@@ -147,4 +148,11 @@ export const games = [
urlTerm: "meme-caption-maker",
element: ,
},
+ {
+ title: "Flappy Bird",
+ description: "Fly the bird and avoid obstacles!",
+ icon: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSap9rEhSD7ghcjTSYN6HuXx0wejnzigvKncg&s",
+ urlTerm: "FlappyBird",
+ element: ,
+ }
];
\ No newline at end of file
diff --git a/src/pages/games/FlappyBird.js b/src/pages/games/FlappyBird.js
new file mode 100644
index 0000000..34aafda
--- /dev/null
+++ b/src/pages/games/FlappyBird.js
@@ -0,0 +1,343 @@
+import React, { useRef, useState, useEffect } from "react";
+
+
+const styles = {
+ container: {
+ fontFamily: "'Trebuchet MS', sans-serif",
+ background: "linear-gradient(135deg, #00b4d8 0%, #90e0ef 100%)",
+ height: "100vh",
+ width: "100vw",
+ overflow: "hidden",
+ position: "relative",
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "center"
+ },
+ screen: {
+ background: "rgba(255,255,255,0.95)",
+ borderRadius: "18px",
+ boxShadow: "0 4px 24px rgba(0,0,0,0.15)",
+ padding: "32px 38px",
+ minWidth: "340px",
+ textAlign: "center",
+ animation: "fadeInScreen 0.7s"
+ },
+ btn: {
+ margin: "12px",
+ padding: "10px 28px",
+ borderRadius: "16px",
+ border: "none",
+ background: "linear-gradient(90deg,#00b4d8,#48cae4)",
+ color: "#fff",
+ fontSize: "18px",
+ cursor: "pointer",
+ fontWeight: 500,
+ transition: "background .2s"
+ },
+ canvasBox: {
+ borderRadius: "14px",
+ overflow: "hidden",
+ boxShadow: "0 4px 16px rgba(68,202,228,0.12)",
+ },
+};
+
+const DIFF_SETTINGS = {
+ Easy: { pipeGap: 170, pipeSpeed: 2, duration: 45 },
+ Medium: { pipeGap: 140, pipeSpeed: 2.6, duration: 40 },
+ Hard: { pipeGap: 110, pipeSpeed: 3.6, duration: 35 },
+};
+
+const CANVAS_WIDTH = 420;
+const CANVAS_HEIGHT = 500;
+
+function randomPipeY(gap) {
+
+ return 80 + Math.random() * (CANVAS_HEIGHT - gap - 120);
+}
+
+const welcomeText = (
+
+
Welcome to Flappy Bird!
+
+ How To Play:
+ Tap/press Space or click to make the bird .
+ Avoid hitting pipes — survive as long as you can.
+ Game lasts for a set duration depending on difficulty.
+ Score points for passing pipes!
+
+
+);
+
+
+function FlappyBirdGame({ difficulty, onGameEnd }) {
+ const canvasRef = useRef(null);
+ const [score, setScore] = useState(0);
+ const [isRunning, setIsRunning] = useState(true);
+ const [secondsLeft, setSecondsLeft] = useState(DIFF_SETTINGS[difficulty].duration);
+
+
+ const bird = useRef({
+ x: 70,
+ y: CANVAS_HEIGHT/2,
+ vy: 0,
+ radius: 18
+ });
+
+ const pipes = useRef([
+ { x: CANVAS_WIDTH + 40, y: randomPipeY(DIFF_SETTINGS[difficulty].pipeGap) }
+ ]);
+
+
+ useEffect(() => {
+
+ let timer = setInterval(() => {
+ setSecondsLeft(sec => {
+ if (sec > 0 && isRunning) return sec - 1;
+ return sec;
+ });
+ }, 1000);
+
+ return () => clearInterval(timer);
+ }, [difficulty, isRunning]);
+
+ useEffect(() => {
+ if (secondsLeft === 0 && isRunning) {
+ setIsRunning(false);
+ setTimeout(() => onGameEnd(score), 530);
+ }
+ }, [secondsLeft, isRunning, onGameEnd, score]);
+
+
+ useEffect(() => {
+ const jump = () => {
+ if (isRunning) bird.current.vy = -4.8;
+ };
+ window.addEventListener('keydown', e => {
+ if (e.code === 'Space') jump();
+ });
+ window.addEventListener('mousedown', jump);
+ return () => {
+ window.removeEventListener('keydown', () => {});
+ window.removeEventListener('mousedown', () => {});
+ };
+ }, [isRunning]);
+
+
+ useEffect(() => {
+ let requestId;
+ const ctx = canvasRef.current.getContext("2d");
+ let lastPassed = 0;
+
+ function draw() {
+
+ ctx.fillStyle = "#caf0f8";
+ ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
+
+
+ for (let c=0; c<3; ++c) {
+ ctx.globalAlpha = 0.39 + 0.25*Math.cos(Date.now()/700 + c*2);
+ ctx.beginPath();
+ ctx.arc((c*140+50 + Date.now()/c)/1.8 % CANVAS_WIDTH, 60+36*c, 36+c*10, 0, Math.PI*2);
+ ctx.fillStyle = "#fff";
+ ctx.fill();
+ ctx.globalAlpha = 1;
+ }
+
+
+ ctx.save();
+ ctx.shadowColor = "#90e0ef80";
+ ctx.shadowBlur = 12;
+ pipes.current.forEach((pipe,i) => {
+ ctx.fillStyle = "#00b4d8";
+
+ ctx.fillRect(pipe.x, 0, 52, pipe.y);
+ ctx.strokeStyle = "#90e0ef";
+ ctx.strokeRect(pipe.x, 0, 52, pipe.y);
+
+ ctx.fillRect(pipe.x, pipe.y + DIFF_SETTINGS[difficulty].pipeGap, 52, CANVAS_HEIGHT - pipe.y - DIFF_SETTINGS[difficulty].pipeGap);
+ ctx.strokeRect(pipe.x, pipe.y + DIFF_SETTINGS[difficulty].pipeGap, 52, CANVAS_HEIGHT - pipe.y - DIFF_SETTINGS[difficulty].pipeGap);
+ });
+ ctx.restore();
+
+
+ ctx.save();
+ ctx.globalAlpha = 0.25;
+ ctx.beginPath();
+ ctx.arc(bird.current.x, bird.current.y + 18, bird.current.radius, 0, Math.PI * 2);
+ ctx.fillStyle = "#888";
+ ctx.fill();
+ ctx.restore();
+
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.arc(bird.current.x, bird.current.y, bird.current.radius, 0, Math.PI*2);
+ ctx.fillStyle = "#ffbe0b";
+ ctx.strokeStyle = "#ffd60a";
+ ctx.lineWidth = 3 + 2*Math.abs(Math.sin(Date.now()/400));
+ ctx.fill();
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.arc(bird.current.x+8, bird.current.y-6, 4, 0, Math.PI*2);
+ ctx.fillStyle = "#fff";
+ ctx.fill();
+ ctx.beginPath();
+ ctx.arc(bird.current.x+10, bird.current.y-6, 1.5, 0, Math.PI*2);
+ ctx.fillStyle = "#111";
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.ellipse(bird.current.x-8, bird.current.y, 13, 7 + 8*Math.abs(Math.cos(Date.now()/260)), 0, 0, Math.PI*2);
+ ctx.fillStyle = "#fdfcdc";
+ ctx.fill();
+ ctx.restore();
+
+
+ ctx.save();
+ ctx.font = "21px Trebuchet MS";
+ ctx.fillStyle = "#0077b6";
+ ctx.fillText(`Score: ${score}`, 24, 42);
+ ctx.fillStyle = "#03045e";
+ ctx.fillText(`Time: ${secondsLeft}s`, CANVAS_WIDTH-120, 42);
+ ctx.restore();
+ }
+
+ function gameLoop() {
+ if (!isRunning) return;
+
+ pipes.current.forEach(pipe => pipe.x -= DIFF_SETTINGS[difficulty].pipeSpeed);
+
+
+ if (pipes.current.length && pipes.current[0].x < -52) pipes.current.shift();
+ let lastPipe = pipes.current[pipes.current.length - 1];
+ if (lastPipe.x < CANVAS_WIDTH - 180) {
+ pipes.current.push({
+ x: CANVAS_WIDTH + 44,
+ y: randomPipeY(DIFF_SETTINGS[difficulty].pipeGap)
+ });
+ }
+
+
+ bird.current.vy += 0.34;
+ bird.current.y += bird.current.vy;
+
+
+ if (bird.current.y > CANVAS_HEIGHT- bird.current.radius) {
+ bird.current.y = CANVAS_HEIGHT- bird.current.radius;
+ bird.current.vy = 0;
+ setIsRunning(false);
+ setTimeout(()=>onGameEnd(score),450);
+ }
+ if (bird.current.y < bird.current.radius) {
+ bird.current.y = bird.current.radius + 3;
+ bird.current.vy = 0.5;
+ }
+
+
+ for (let i=0; i pipeX && cx - r < pipeX + pipeW) {
+ if (cy - r < gapY || cy + r > gapY + gapH) {
+ setIsRunning(false);
+ setTimeout(()=>onGameEnd(score),480);
+ break;
+ }
+ }
+ }
+
+
+ pipes.current.forEach((pipe,idx) => {
+ if (!pipe.passed && bird.current.x > pipe.x + 52) {
+ pipe.passed = true;
+ setScore(s => s + 1);
+ }
+ });
+
+ draw();
+ requestId = requestAnimationFrame(gameLoop);
+ }
+
+ draw();
+ requestId = requestAnimationFrame(gameLoop);
+
+ return () => cancelAnimationFrame(requestId);
+ }, [difficulty, isRunning, onGameEnd, score, secondsLeft]);
+
+ return (
+
+
+
+ );
+}
+
+
+export default function FlappyBirdMiniGame() {
+ const [screen, setScreen] = useState("welcome");
+ const [difficulty, setDifficulty] = useState(null);
+ const [lastScore, setLastScore] = useState(0);
+
+ return (
+
+ {screen === "welcome" &&
+
+ {welcomeText}
+
+
Select Difficulty:
+
+ {Object.keys(DIFF_SETTINGS).map(diff => (
+ {
+ setDifficulty(diff);
+ setScreen("game");
+ }}
+ >{diff}
+ ))}
+
+
+
+ }
+ {screen === "game" &&
+
{
+ setLastScore(score);
+ setScreen("result");
+ }}
+ />
+ }
+ {screen === "result" &&
+
+
Game Over!
+
+ Final Score: {lastScore}
+
+
Ready for another round?
+
setScreen("welcome")}
+ >Play Again
+
+ }
+
+
+ );
+}