Add hyperspace starfield to landing page
deploy / deploy (push) Successful in 4m46s
cleanup-expired / cleanup-expired (push) Successful in 1s

Replaces the static Backstage template page with a canvas-based warp
starfield and a glowing "SETH'S TEST" HUD overlay. Self-contained, no
external assets; star count is fixed and resize-safe.
This commit is contained in:
seth
2026-05-13 14:04:01 -05:00
parent 6cb533c3ff
commit f659b3e58d
2 changed files with 169 additions and 31 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ def index():
return render_template(
"index.html",
name="sethtest",
description="Seth doing stuff",
description="Warp drive engaged",
)
+168 -30
View File
@@ -3,50 +3,188 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{{ name }}</title>
<title>{{ name }} — warp drive</title>
<style>
html,
body {
margin: 0;
font-family: Arial, sans-serif;
color: #0f172a;
background: #f8fafc;
padding: 0;
height: 100%;
background: #000;
color: #cfe7ff;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
overflow: hidden;
}
main {
max-width: 760px;
margin: 6rem auto;
padding: 0 1.5rem;
canvas#stars {
position: fixed;
inset: 0;
width: 100vw;
height: 100vh;
display: block;
}
section {
background: #ffffff;
border: 1px solid #dbe3ee;
border-radius: 8px;
padding: 2rem;
.hud {
position: fixed;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
pointer-events: none;
text-align: center;
}
h1 {
margin-top: 0;
.title {
font-size: clamp(2.5rem, 8vw, 6rem);
letter-spacing: 0.35em;
font-weight: 700;
color: #e8f6ff;
text-shadow:
0 0 8px #6cb9ff,
0 0 24px #2b7bd6,
0 0 64px #103a78;
animation: pulse 4s ease-in-out infinite;
}
p,
li {
line-height: 1.6;
color: #334155;
.subtitle {
margin-top: 1.5rem;
font-size: 0.95rem;
letter-spacing: 0.4em;
opacity: 0.7;
text-transform: uppercase;
}
.meta {
position: fixed;
bottom: 1rem;
left: 1rem;
font-size: 0.75rem;
letter-spacing: 0.2em;
opacity: 0.5;
text-transform: uppercase;
}
@keyframes pulse {
0%,
100% {
text-shadow:
0 0 8px #6cb9ff,
0 0 24px #2b7bd6,
0 0 64px #103a78;
}
50% {
text-shadow:
0 0 12px #9fd2ff,
0 0 36px #4aa0ff,
0 0 96px #1d57b3;
}
}
</style>
</head>
<body>
<main>
<section>
<h1>{{ name }}</h1>
<p>{{ description }}</p>
<ul>
<li>Backed by Flask and Gunicorn</li>
<li>Container listens on port 8080</li>
<li>Health check is available at <code>/health</code></li>
</ul>
</section>
</main>
<canvas id="stars"></canvas>
<div class="hud">
<div class="title">SETH&apos;S&nbsp;TEST</div>
<div class="subtitle">{{ description }}</div>
</div>
<div class="meta">{{ name }} &middot; /health</div>
<script>
(function () {
const canvas = document.getElementById("stars");
if (!canvas || !canvas.getContext) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
const STAR_COUNT = 400;
const MAX_Z = 1.0;
const SPEED = 0.006;
const FOCAL = 320;
let width = 0;
let height = 0;
let cx = 0;
let cy = 0;
let stars = [];
function resize() {
width = window.innerWidth;
height = window.innerHeight;
const dpr = window.devicePixelRatio || 1;
canvas.width = Math.floor(width * dpr);
canvas.height = Math.floor(height * dpr);
canvas.style.width = width + "px";
canvas.style.height = height + "px";
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
cx = width / 2;
cy = height / 2;
}
function spawn() {
const z = Math.random() * (MAX_Z - 0.2) + 0.2;
return {
x: (Math.random() - 0.5) * 2,
y: (Math.random() - 0.5) * 2,
z: z,
pz: z
};
}
function reset(s) {
s.x = (Math.random() - 0.5) * 2;
s.y = (Math.random() - 0.5) * 2;
s.z = MAX_Z;
s.pz = MAX_Z;
}
function project(x, y, z) {
return { sx: (x / z) * FOCAL + cx, sy: (y / z) * FOCAL + cy };
}
function frame() {
// Trail effect: fade the previous frame instead of clearing.
ctx.fillStyle = "rgba(0, 0, 0, 0.25)";
ctx.fillRect(0, 0, width, height);
ctx.lineCap = "round";
for (let i = 0; i < stars.length; i++) {
const s = stars[i];
s.pz = s.z;
s.z -= SPEED;
if (s.z <= 0.02) {
reset(s);
continue;
}
const p = project(s.x, s.y, s.z);
if (
p.sx < -50 ||
p.sx > width + 50 ||
p.sy < -50 ||
p.sy > height + 50
) {
reset(s);
continue;
}
const pp = project(s.x, s.y, s.pz);
const k = 1 - s.z / MAX_Z;
const w = Math.max(0.4, k * 2.2);
ctx.strokeStyle = "rgba(220, 240, 255, " + (0.3 + k * 0.7) + ")";
ctx.lineWidth = w;
ctx.beginPath();
ctx.moveTo(pp.sx, pp.sy);
ctx.lineTo(p.sx, p.sy);
ctx.stroke();
}
requestAnimationFrame(frame);
}
window.addEventListener("resize", resize);
resize();
stars = new Array(STAR_COUNT);
for (let i = 0; i < STAR_COUNT; i++) stars[i] = spawn();
requestAnimationFrame(frame);
})();
</script>
</body>
</html>