Skip to content

Feat(platform): Kong easter egg#5255

Open
Guaris wants to merge 2 commits into
mainfrom
easter-egg
Open

Feat(platform): Kong easter egg#5255
Guaris wants to merge 2 commits into
mainfrom
easter-egg

Conversation

@Guaris
Copy link
Copy Markdown
Contributor

@Guaris Guaris commented May 15, 2026

No description provided.

Copilot AI review requested due to automatic review settings May 15, 2026 18:15
@Guaris Guaris requested a review from a team as a code owner May 15, 2026 18:15
@netlify
Copy link
Copy Markdown

netlify Bot commented May 15, 2026

Deploy Preview for kongdeveloper ready!

Name Link
🔨 Latest commit 90d1ac0
🔍 Latest deploy log https://app.netlify.com/projects/kongdeveloper/deploys/6a07682855e3760008be29a0
😎 Deploy Preview https://deploy-preview-5255--kongdeveloper.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a hidden "bullet hell" easter egg to the Kong Developer docs site. When a desktop visitor scrolls to the bottom of a long page, scrolls away, then back, the page's SVG icons morph into enemy sprites and launch a full-screen Touhou-style mini-game rendered on a canvas overlay.

Changes:

  • New app/_assets/javascripts/bullet_hell/ module (~1100 LOC) implementing the game loop, sprite morph, bullet patterns, HUD, and player/boss/minion mechanics.
  • New CSS keyframe bullet-hell-shake for the page-shake telegraph.
  • Wires the new module into the global application.js entrypoint.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
app/_assets/entrypoints/application.js Imports the new bullet_hell module so it runs on every page.
app/_assets/stylesheets/index.css Adds keyframes and class for the page-shake trigger.
app/_assets/javascripts/bullet_hell/index.js Scroll/state machine that detects the trigger, freezes scroll, and starts the game.
app/_assets/javascripts/bullet_hell/morph.js Captures qualifying page SVGs, animates clones into boss formation, returns sprite Images.
app/_assets/javascripts/bullet_hell/patterns.js Boss bullet patterns (aimed, spread, ring, stream) with seeded jitter.
app/_assets/javascripts/bullet_hell/game.js Main Game class: input, fixed-step loop, boss/minion AI, powerups, bombs, HUD, menus, rendering.

Comment on lines +98 to +105
function svgToImage(html) {
const img = new Image();
try {
img.src = `data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(html)))}`;
} catch (_) {
img.src = "data:image/svg+xml;utf8," + encodeURIComponent(html);
}
return img;
Comment on lines +34 to +71
let activeGame = null;
let prevY = scroller().scrollTop;

window.addEventListener("scroll", () => {
if (state === "running") return;

const y = scroller().scrollTop;
const goingDown = y > prevY;
prevY = y;

if (state === "idle" && goingDown && atBottom()) {
state = "shaken";
document.body.classList.add("bullet-hell-shake");
setTimeout(() => document.body.classList.remove("bullet-hell-shake"), SHAKE_MS);
return;
}

if (state === "shaken" && scrolledAway()) {
state = "armed";
return;
}

if (state === "armed" && goingDown && atBottom()) {
state = "running";
start();
}
}, { passive: true });

async function start() {
document.documentElement.style.overflow = "hidden";

const game = new Game([], () => {
document.documentElement.style.overflow = "";
activeGame = null;
state = "idle";
});
game.mountChrome();
activeGame = game;
Comment on lines +65 to +80
const game = new Game([], () => {
document.documentElement.style.overflow = "";
activeGame = null;
state = "idle";
});
game.mountChrome();
activeGame = game;

const { sprites, restore } = await morph();
game.onExit = (orig => () => {
restore();
orig();
})(game.onExit);

game.setSprites(sprites);
game.start();
Comment on lines +30 to +40
if (isMobile()) return;
if (scroller().scrollHeight <= window.innerHeight + BOTTOM_THRESHOLD) return;

let state = "idle";
let activeGame = null;
let prevY = scroller().scrollTop;

window.addEventListener("scroll", () => {
if (state === "running") return;

const y = scroller().scrollTop;
Comment on lines +8 to +13
function isMobile() {
return (
window.matchMedia("(pointer: coarse)").matches ||
window.innerWidth < 768
);
}
Comment on lines +12 to +13
const STAGES_TOTAL = 3;
const STAGE_BOSS_COUNTS = [0, 2, 2, 3];
return { el, rect, html: serializeSvg(el) };
});

for (const c of captured) c.el.style.visibility = "hidden";
Comment on lines +284 to +305
this.onKeyDown = (e) => {
if (e.key === "Escape") {
e.preventDefault();
this.exit();
return;
}
if (e.key === "r" || e.key === "R") {
if (this.state === "lost") this.restart();
return;
}
if (e.key === "n" || e.key === "N") {
if (this.state === "stage-clear") this.nextStage();
return;
}
if (e.key === "x" || e.key === "X" || e.key === "b" || e.key === "B") {
e.preventDefault();
this.fireBomb();
return;
}
this.keys.add(e.key);
};
this.onKeyUp = (e) => this.keys.delete(e.key);
Comment on lines +250 to +266
this.hud.innerHTML = `
<div data-hud="banner" style="position:absolute;top:38%;left:32px;max-width:440px;color:#fff;font-family:'Inter',system-ui,sans-serif;pointer-events:none;transform:translateY(-50%)">
<h1 style="font-size:40px;font-weight:800;line-height:1.15;margin:0 0 16px 0;color:#fff;text-shadow:0 2px 16px rgba(0,0,0,0.95),0 0 4px rgba(0,0,0,0.9)">Kong Developer</h1>
<p style="font-size:16px;line-height:1.55;margin:0;opacity:0.95;text-shadow:0 1px 8px rgba(0,0,0,0.95),0 0 2px rgba(0,0,0,0.9)">Discover Kong's tools, APIs, and tutorials to help you build, secure, and scale your services.</p>
</div>
<div style="display:flex;justify-content:space-between;gap:12px;font-size:14px">
<div style="display:flex;flex-direction:column;gap:6px;align-items:flex-start">
<span data-hud="score" style="${chip}">SCORE 0</span>
<span data-hud="power" style="${chip}">POWER ●○○○</span>
</div>
<div style="display:flex;flex-direction:column;gap:6px;align-items:flex-end">
<span data-hud="bombs" style="${chip}">SPECIAL ATTACK ●●●</span>
<span style="${chip};opacity:0.85">arrows · Z fire · X special · shift focus · esc exit</span>
</div>
</div>
<div data-hud="overlay" style="text-align:center;font-size:24px"></div>
`;
Comment on lines +268 to +270
this.resize = () => {
this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants