diff --git a/README.md b/README.md index 80bca61..0e7daf8 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,13 @@ Personal website and blog of Aleksandr Albekov — live at **[bulbashenko.com](https://bulbashenko.com)**. -A retro terminal-styled single page with a CRT filter and a weekly pre-rendered ASCII skull background, plus a small admin panel for editing everything on it. +A retro terminal-styled single page with a CRT filter and an animated ASCII skull background, plus a small admin panel for editing everything on it. ## Features - Profile, blog posts (Markdown), projects, gallery and CV in English, Russian and Slovak. - Admin panel at `/admin`: content editing, image uploads, active session management. Sign-in goes through the single sign-on at `auth.bulbashenko.com` ([bulbashenko/auth](https://github.com/bulbashenko/auth)); members of the `site-admins` group get in. -- Weekly ASCII skull video background, rendered in GitHub Actions with Playwright and ffmpeg (`scripts/skull-render`). +- ASCII skull background drawn live with WebGL2 in the active palette (`src/components/public/background`); the skull under the pointer glitches. Browsers without WebGL2, or too slow to keep up, get a weekly video of the same scene, rendered in GitHub Actions with Playwright and ffmpeg (`scripts/skull-render`). - Dynamic Open Graph image, sitemap and robots. ## Stack diff --git a/package-lock.json b/package-lock.json index 2a31cca..341dee6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "react-markdown": "^10.1.0", "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.1", + "three": "0.186.1", "zod": "^4.3.6" }, "devDependencies": { @@ -9554,6 +9555,12 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/three": { + "version": "0.186.1", + "resolved": "https://registry.npmjs.org/three/-/three-0.186.1.tgz", + "integrity": "sha512-blFeqb49wRCSGUGj7gtpfnSGHy2lwDk94RhUmS1c/hTby70kvChbWpkJ4Pm1390LqzzvTmzgXKHPEafJwCb8jA==", + "license": "MIT" + }, "node_modules/tinyglobby": { "version": "0.2.16", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", diff --git a/package.json b/package.json index 4cc2118..c61107c 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "react-markdown": "^10.1.0", "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.1", + "three": "0.186.1", "zod": "^4.3.6" }, "devDependencies": { diff --git a/scripts/skull-render/package-lock.json b/scripts/skull-render/package-lock.json index ca21433..8f83d75 100644 --- a/scripts/skull-render/package-lock.json +++ b/scripts/skull-render/package-lock.json @@ -7,9 +7,9 @@ "name": "skull-render", "dependencies": { "@aws-sdk/client-s3": "^3.1146.0", - "@fontsource/jetbrains-mono": "^5.2.5", + "@fontsource/ibm-plex-mono": "5.3.0", "playwright": "^1.55.0", - "three": "0.184.0" + "three": "0.186.1" } }, "node_modules/@aws-sdk/checksums": { @@ -320,10 +320,10 @@ "node": ">=18.0.0" } }, - "node_modules/@fontsource/jetbrains-mono": { + "node_modules/@fontsource/ibm-plex-mono": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@fontsource/jetbrains-mono/-/jetbrains-mono-5.3.0.tgz", - "integrity": "sha512-fqDfB5I9f1p1TV486aUgB9t8zP84P0O1FtQR5Ol9vjwPy+S+EIGlVYm1cvj2W5shcZMTg2nZFdVMoH5wFu8a1A==", + "resolved": "https://registry.npmjs.org/@fontsource/ibm-plex-mono/-/ibm-plex-mono-5.3.0.tgz", + "integrity": "sha512-eTgnZjZEGk1QtD3ZstF+Vclo2HLAni8YMy34/DxllwZvyz1lR/1RF/xTiAquOBO7MvqBx8D2Ig2WCPMVfdZu7Q==", "license": "OFL-1.1", "funding": { "url": "https://github.com/sponsors/ayuhito" @@ -444,9 +444,9 @@ } }, "node_modules/three": { - "version": "0.184.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.184.0.tgz", - "integrity": "sha512-wtTRjG92pM5eUg/KuUnHsqSAlPM296brTOcLgMRqEeylYTh/CdtvKUvCyyCQTzFuStieWxvZb8mVTMvdPyUpxg==", + "version": "0.186.1", + "resolved": "https://registry.npmjs.org/three/-/three-0.186.1.tgz", + "integrity": "sha512-blFeqb49wRCSGUGj7gtpfnSGHy2lwDk94RhUmS1c/hTby70kvChbWpkJ4Pm1390LqzzvTmzgXKHPEafJwCb8jA==", "license": "MIT" }, "node_modules/tslib": { diff --git a/scripts/skull-render/package.json b/scripts/skull-render/package.json index 31c4d51..7b9815e 100644 --- a/scripts/skull-render/package.json +++ b/scripts/skull-render/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@aws-sdk/client-s3": "^3.1146.0", - "@fontsource/jetbrains-mono": "^5.2.5", + "@fontsource/ibm-plex-mono": "5.3.0", "playwright": "^1.55.0", - "three": "0.184.0" + "three": "0.186.1" } } diff --git a/scripts/skull-render/serve.mjs b/scripts/skull-render/serve.mjs index e15c033..b4f1d50 100644 --- a/scripts/skull-render/serve.mjs +++ b/scripts/skull-render/serve.mjs @@ -1,17 +1,21 @@ -// Minimal static server for skull.html (preview and headless rendering). +// Minimal static server for skull.html (preview and headless rendering). Also serves the site's background elements. import { createServer } from "node:http"; import { readFile } from "node:fs/promises"; import { extname, join, normalize } from "node:path"; import { fileURLToPath } from "node:url"; const ROOT = fileURLToPath(new URL(".", import.meta.url)); +const SITE = fileURLToPath(new URL("../../", import.meta.url)); +// The elements skull.html renders, from the site's source tree. +const SHARED = "/src/components/public/background/"; const TYPES = { ".html": "text/html", ".js": "text/javascript", ".mjs": "text/javascript", ".woff2": "font/woff2", ".json": "application/json" }; export function serve(port = 0) { const server = createServer(async (req, res) => { const path = normalize(decodeURIComponent(new URL(req.url, "http://x").pathname)).replace(/^(\.\.[/\\])+/, ""); + const file = path.startsWith(SHARED) ? join(SITE, path) : join(ROOT, path === "/" ? "skull.html" : path); try { - const body = await readFile(join(ROOT, path === "/" ? "skull.html" : path)); + const body = await readFile(file); res.writeHead(200, { "Content-Type": TYPES[extname(path)] ?? "application/octet-stream" }); res.end(body); } catch { diff --git a/scripts/skull-render/skull.html b/scripts/skull-render/skull.html index 522a643..1fcf334 100644 --- a/scripts/skull-render/skull.html +++ b/scripts/skull-render/skull.html @@ -3,289 +3,59 @@
-