summaryrefslogtreecommitdiff
path: root/internal/generator/theme_synthwave.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-04-10 09:22:06 +0300
committerPaul Buetow <paul@buetow.org>2026-04-10 09:22:06 +0300
commitf3155cbc866a1b261a0aafe61683da1e3c25b1ef (patch)
treededad4708adf0b377dd06c11c1bc957bc1bfe302 /internal/generator/theme_synthwave.go
parent3e61d09873065f5342efc414ee3ea0d5fdc4c767 (diff)
add WebGL scenes to all themes, sounds, image sizing, new themes
- All 11 themes now have unique Three.js WebGL backgrounds: aurora: flowing sine-wave ribbon meshes with additive blending brutalist: harsh rotating white/red wireframe boxes glass: drifting crystal icosahedron shards, semi-transparent matrix: digital rain particle columns with per-vertex colour fade ocean: animated vertex-displaced wave surface with orbiting light retro: amber demo-scene cube with orbiting octahedrons synthwave: sunset sphere with scan-line rings and perspective grid terminal: green icosahedron wireframe with orbiting torus particles neon: existing Three.js orb and rings (unchanged) plasma (replaces minimal): drifting additive-blend colour blobs volcano (replaces paper): rising ember particles with lifecycle - shared.go: distinct sounds for j/k nav (220Hz beep), Enter (ascending triangle chime), and Esc (descending sine sweep) - shared.go: images are now thumbnails (max-height:220px) in list view and expand to full width inside the modal (CSS override in navmodal) - main.go: --list-themes flag prints all theme names and exits; --theme random picks a random theme at all generation time - themes.go: updated registry with plasma/volcano replacing minimal/paper Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/generator/theme_synthwave.go')
-rw-r--r--internal/generator/theme_synthwave.go110
1 files changed, 94 insertions, 16 deletions
diff --git a/internal/generator/theme_synthwave.go b/internal/generator/theme_synthwave.go
index 8798d18..366c5b5 100644
--- a/internal/generator/theme_synthwave.go
+++ b/internal/generator/theme_synthwave.go
@@ -1,7 +1,8 @@
package generator
-// synthwaveTemplate is the 80s retrowave theme — dark purple sky, CSS perspective
-// grid floor, hot pink/orange accents, Russo One font.
+// synthwaveTemplate is the 80s retrowave theme — dark purple sky, WebGL
+// perspective grid with a glowing sun and scan-line rings, hot pink/orange
+// accents, Russo One font. The CSS sky and grid-floor divs are replaced by WebGL.
const synthwaveTemplate = `<!DOCTYPE html>
<html lang="en">
<head>
@@ -10,21 +11,13 @@ const synthwaveTemplate = `<!DOCTYPE html>
<title>snonux.foo ⊕ SYNTHWAVE</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Russo+One&family=Share+Tech+Mono&display=swap" rel="stylesheet">
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script>
<style>
:root { --pink:#ff2d78; --purple:#bf3fff; --orange:#ff6b2b; --bg:#0d0221; }
* { margin:0; padding:0; box-sizing:border-box; }
body { font-family:'Russo One','Arial Black',sans-serif; background:var(--bg);
color:#fff; overflow:hidden; height:100vh; }
- /* Sunset sky gradient */
- .sky { position:fixed; inset:0; z-index:0;
- background:linear-gradient(180deg,#0d0221 0%,#1a0533 45%,#4a0080 72%,#8b1070 88%,#c8365a 100%); }
- /* Perspective grid floor */
- .grid-floor { position:fixed; bottom:0; left:0; width:100%; height:46vh; z-index:1;
- background-image:linear-gradient(rgba(255,45,120,0.35) 1px,transparent 1px),
- linear-gradient(90deg,rgba(255,45,120,0.35) 1px,transparent 1px);
- background-size:44px 44px;
- transform:perspective(380px) rotateX(76deg); transform-origin:bottom;
- mask-image:linear-gradient(to top,rgba(0,0,0,0.85) 0%,transparent 100%); }
+ #three-canvas { position:fixed; top:0; left:0; width:100%; height:100%; z-index:1; }
.overlay { position:relative; z-index:10; height:100vh; display:flex; flex-direction:column; }
header { padding:16px 28px; background:rgba(13,2,33,0.82); backdrop-filter:blur(10px);
border-bottom:2px solid var(--pink); display:flex; align-items:center; justify-content:space-between; }
@@ -61,7 +54,6 @@ const synthwaveTemplate = `<!DOCTYPE html>
.post-time { color:var(--orange); font-family:'Share Tech Mono',monospace; font-size:0.85rem; }
.post-text { line-height:1.6; font-size:0.95rem; font-family:'Share Tech Mono',monospace; }
.post-text a { color:var(--pink); text-decoration:none; }
- .post-image { max-width:100%; border-radius:6px; margin-top:10px; }
.post-audio { width:100%; margin-top:10px; }
.post-modal { display:none; position:fixed; inset:0; z-index:100;
background:rgba(13,2,33,0.96); overflow-y:auto; padding:40px 20px; }
@@ -71,12 +63,11 @@ const synthwaveTemplate = `<!DOCTYPE html>
box-shadow:0 0 60px rgba(255,45,120,0.35); padding:38px; }
.modal-close { float:right; background:none; border:none; color:var(--orange);
font-family:'Russo One',sans-serif; font-size:0.9rem; cursor:pointer; letter-spacing:2px; }
- @media(max-width:640px) { .nav-hints{display:none;} .grid-floor{height:30vh;} header{padding:12px 18px;} }
+ @media(max-width:640px) { .nav-hints{display:none;} header{padding:12px 18px;} }
</style>
</head>
<body>
- <div class="sky"></div>
- <div class="grid-floor"></div>
+ <canvas id="three-canvas"></canvas>
<div class="overlay">
<header>
<div class="logo">
@@ -106,6 +97,93 @@ const synthwaveTemplate = `<!DOCTYPE html>
</div>
</div>
{{template "navmodal" .}}
+ <script>
+ // Synthwave WebGL: glowing sunset sphere with horizontal scan-line rings,
+ // a receding grid floor, and pink star particles. Replaces CSS sky/grid.
+ (function() {
+ var scene, camera, renderer, clock;
+ var sun, sunRings = [];
+
+ function initThree() {
+ scene = new THREE.Scene();
+ scene.background = new THREE.Color(0x0d0221);
+ scene.fog = new THREE.Fog(0x0d0221, 60, 180);
+
+ camera = new THREE.PerspectiveCamera(60, window.innerWidth/window.innerHeight, 0.1, 300);
+ camera.position.set(0, 10, 45);
+ camera.lookAt(0, -5, -10);
+
+ renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('three-canvas'), antialias: true });
+ renderer.setSize(window.innerWidth, window.innerHeight);
+ renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
+ clock = new THREE.Clock();
+
+ // Glowing orange sunset sphere
+ sun = new THREE.Mesh(
+ new THREE.SphereGeometry(12, 32, 16),
+ new THREE.MeshBasicMaterial({ color: 0xff6b2b })
+ );
+ sun.position.set(0, -8, -55);
+ scene.add(sun);
+
+ // Horizontal scan-line rings — alternating pink/purple, stacked on the sun
+ var ringColors = [0xff2d78, 0xbf3fff, 0xff2d78, 0xbf3fff, 0xff2d78, 0xbf3fff, 0xff2d78, 0xbf3fff];
+ for (var i = 0; i < 8; i++) {
+ var ring = new THREE.Mesh(
+ new THREE.TorusGeometry(13 + i * 1.2, 0.09, 8, 64),
+ new THREE.MeshBasicMaterial({ color: ringColors[i] })
+ );
+ ring.position.copy(sun.position);
+ ring.position.y += -4 + i * 1.1;
+ scene.add(ring);
+ sunRings.push(ring);
+ }
+
+ // Receding grid floor
+ var grid = new THREE.GridHelper(200, 40, 0xff2d78, 0x4a0060);
+ grid.position.set(0, -18, -30);
+ scene.add(grid);
+
+ // 1200 star particles scattered in a sphere shell
+ var starPos = new Float32Array(1200 * 3);
+ for (var j = 0; j < 1200 * 3; j += 3) {
+ var r = 80 + Math.random() * 40;
+ var theta = Math.random() * Math.PI * 2;
+ var phi = Math.acos(2 * Math.random() - 1);
+ starPos[j] = r * Math.sin(phi) * Math.cos(theta);
+ starPos[j+1] = r * Math.sin(phi) * Math.sin(theta);
+ starPos[j+2] = r * Math.cos(phi);
+ }
+ var starGeo = new THREE.BufferGeometry();
+ starGeo.setAttribute('position', new THREE.BufferAttribute(starPos, 3));
+ scene.add(new THREE.Points(starGeo, new THREE.PointsMaterial({
+ color: 0xff88aa, size: 0.2, transparent: true, opacity: 0.7
+ })));
+
+ window.addEventListener('resize', onResize);
+ animate();
+ }
+
+ function onResize() {
+ camera.aspect = window.innerWidth / window.innerHeight;
+ camera.updateProjectionMatrix();
+ renderer.setSize(window.innerWidth, window.innerHeight);
+ }
+
+ function animate() {
+ requestAnimationFrame(animate);
+ var t = clock.getElapsedTime();
+ // Sun pulses subtly; camera drifts sideways for parallax
+ var pulse = 1 + 0.015 * Math.sin(t * 1.5);
+ sun.scale.setScalar(pulse);
+ sunRings.forEach(function(r) { r.scale.setScalar(pulse); });
+ camera.position.x = Math.sin(t * 0.08) * 4;
+ renderer.render(scene, camera);
+ }
+
+ initThree();
+ })();
+ </script>
{{template "navscript" .}}
</body>
</html>`