memory-palace
memory-palace
Description
Reproducible kit for mounting Henry Heffernan's 3D-CRT-monitor + Win98-desktop portfolio template (`/palace`) on top of an existing Next.js site. Ships drop-in build scripts, config snippets (webpack / Next / Vercel / tsconfig), placeholder showcase pages, NOTICE templates, a post-deploy DevTools verification snippet, and the 13-gotcha PLAYBOOK that catalogues every way this rollout breaks in production. Use when the user wants a /palace-style 3D portfolio with a clickable retro monitor mounted on their own site, when integrating a vendored CRA app inside a Next.js host, or when debugging "same-origin iframe but Chrome treats it as cross-origin", iframe stuck on broken-document, framer-motion type drift on Vercel, COOP/X-Frame-Options blocking own-iframe, or font fallback inside the inner OS.
SKILL.md
Memory Palace — copy-paste kit + production playbook
Live demo: bingranyou.com/palace — what you're aiming for.
Sibling skill: win98-paper-theme makes the rest of your site (front door, blog, every other route) speak the same OS dialect as /palace, so the hand-off between the 3D scene and the docs / posts feels like the same machine. Ship that one after this one if you want full visual cohesion.
What this skill ships
A complete, copy-paste-ready kit for cloning henryjeff/portfolio-website (the outer 3D CRT room) + henryjeff/portfolio-inner-site (the inner Win98 desktop) and mounting them under /palace on your own Next.js site, with your own content.
Two layers:
- Mechanical scaffolding — drop-in build script, config snippets, placeholder content, NOTICE templates. Live under
assets/. Seeassets/INDEX.mdfor the full map. - Production knowledge — 13 gotchas that each cost hours to debug if you don't know them coming. Live in
PLAYBOOK.md.
The mechanical part is mechanical. The reason this skill exists is the 13 gotchas. Read PLAYBOOK.md before deploying. Skipping it means you'll find them yourself.
When to use
- A user wants a 3D retro portfolio with a clickable CRT monitor (like henryheffernan.com)
- A user is mounting any vendored CRA / webpack app inside a Next.js host
- A user is debugging
iframe.contentWindow.locationthrowing cross-origin on same-origin frames - A user is debugging Vercel build failures in a vendored sub-app that build fine locally
- A user wants to swap content in this template without paraphrasing Henry's autobiographical prose
Prerequisites — read before you start
- Licensing.
- Outer (
portfolio-website): MIT. Vendor it withLICENSE.md+NOTICE.mdpreserved. Templates atassets/notice/NOTICE.outer.template.md. - Inner (
portfolio-inner-site): no license file upstream. You need explicit permission from Henry. Without it, build your own inner site from scratch using only the architectural pattern. Template atassets/notice/NOTICE.inner.template.md.
- Outer (
- Host. Next.js (this kit assumes Next.js + Vercel). Any static host works with minor adaptation to the rewrites + headers.
- Disk / bandwidth. The vendored outer ships ~50 MB of 3D models, textures, audio. Vercel free tier handles it.
- Cosmetic limit. The CRT chassis 3D mesh has "Heffernan henry inc" baked into the texture at
palace-outer/static/models/Computer/baked_computer.jpg. You can't fix it with code — re-bake in Blender, Photoshop the texture, or live with it. Document this in your README.
Architecture in one diagram
host/ ← your existing Next.js / static site
├── app/ ← your existing pages
├── public/palace/ ← generated, gitignored
│ ├── index.html (outer entry — webpack build)
│ ├── bundle.<hash>.js
│ ├── models/ textures/ audio/
│ └── os/ ← inner CRA build
│ ├── index.html
│ └── static/js static/css static/media
├── palace-outer/ ← vendored from portfolio-website (MIT)
│ ├── LICENSE.md NOTICE.md
│ ├── src/Application/ (Three.js + GLSL + UI)
│ ├── bundler/ (webpack config)
│ └── static/ (3D assets)
├── palace-inner/ ← vendored from portfolio-inner-site (w/ permission)
│ ├── NOTICE.md
│ ├── src/components/{os,applications,showcase,general}
│ └── public/
├── scripts/build-palace.mjs ← stitches the two builds into public/palace/
├── next.config.ts ← rewrites /palace, /palace/os to index.html
└── vercel.json ← header overrides (see PLAYBOOK §H + §I + §J)
Outer (webpack) builds → public/palace/. Inner (CRA) builds → palace-inner/build/ which then gets copied to public/palace/os/. Outer's MonitorScreen.ts iframes /palace/os to draw the inner site inside the 3D CRT.
30-minute quick start
From the root of your existing Next.js site:
# 1. Bootstrap: clone the two upstreams, strip git history, drop dead weight
bash <(curl -L https://raw.githubusercontent.com/<this-skill-host>/assets/scripts/setup.sh)
# (or, if you have this skill checked out locally, run the file directly:
# bash <path-to>/repo-skills/memory-palace/assets/scripts/setup.sh)
# 2. Drop the build script into scripts/
cp <skill>/assets/scripts/build-palace.mjs scripts/
# 3. Apply config snippets — see assets/INDEX.md for exact targets
# (webpack output, ts-loader transpileOnly, palace-inner tsconfig replace,
# host tsconfig exclude, next rewrites, vercel headers, font + li CSS,
# iframe cache-bust)
# 4. Swap content
cp <skill>/assets/content/*.tsx palace-inner/src/components/showcase/
cp <skill>/assets/content/ShutdownSequence.tsx palace-inner/src/components/os/
# 5. Copy NOTICEs and edit author / permission lines
cp <skill>/assets/notice/NOTICE.outer.template.md palace-outer/NOTICE.md
cp <skill>/assets/notice/NOTICE.inner.template.md palace-inner/NOTICE.md
# 6. Wire host package.json
# "scripts": { "palace:build": "node scripts/build-palace.mjs",
# "build": "npm run palace:build && next build" }
# 7. Build + run
npm run build && npm start
# Visit http://localhost:3000/palace
After deploy, run assets/verification/devtools.js in DevTools at /palace. If any check fails, the matching gotcha in PLAYBOOK.md tells you why.
Required content swaps (don't skip these)
In order of visibility — all paths relative to your vendored copy:
palace-inner/src/components/showcase/{Home,About,Projects,Contact,Experience,VerticalNavbar,ResumeDownload}.tsx— rewrite with your own content. Placeholders inassets/content/.palace-inner/src/components/applications/ShowcaseExplorer.tsx—windowTitle,bottomLeftTextpalace-inner/src/components/os/Toolbar.tsx—HeffernanOS→ your OS name (the start-menu vertical text)palace-inner/src/components/os/Desktop.tsx—APPLICATIONSmap: drop the apps you don't want (Doom / OregonTrail / Scrabble / Henordle / Credits / ThisComputer)palace-inner/public/index.html—<title>, manifest linkpalace-inner/public/manifest.json—name,short_namepalace-outer/src/index.html—<title>, OG/Twitter meta, remove or replace Henry's GA tracking IDG-4FJBF6WF60palace-outer/src/Application/UI/components/LoadingScreen.tsx— BIOS branding (HHBIOS,HSP S13,Heffernan, Henry Inc.)palace-outer/src/Application/UI/components/InfoOverlay.tsx—NAME_TEXT/TITLE_TEXT(top-left HUD)palace-outer/src/Application/World/MonitorScreen.tsline ~207 —iframe.titlepalace-outer/src/Application/Audio/AudioSources.ts—AmbienceAudio: short-circuit if you don't want background music (auto-playsoffice.mp3onloadingScreenDone)palace-outer/src/Application/sources.ts— prefix every asset URL with/palace/(runtime fetches in source files are not rewritten by webpack'spublicPath)palace-inner/src/components/os/ShutdownSequence.tsx— Henry's shutdown sequence is a multi-page autobiographical narrative. Don't translate it with name-swaps. Use the minimalassets/content/ShutdownSequence.tsxor drop the route.
Asset map
| Path | What it is | Where it goes in host |
|---|---|---|
assets/scripts/build-palace.mjs |
Build orchestrator | host/scripts/build-palace.mjs |
assets/scripts/setup.sh |
Clone + strip + dead-weight cleanup | run from host root, no install |
assets/patches/webpack.output.snippet.js |
Replace output{} block |
palace-outer/bundler/webpack.common.js |
assets/patches/webpack.tsloader.snippet.js |
transpileOnly: true on ts-loader (PLAYBOOK §C) |
same file |
assets/patches/palace-inner.tsconfig.json |
Full replacement (PLAYBOOK §E) | palace-inner/tsconfig.json |
assets/patches/host.tsconfig.exclude.snippet.json |
exclude palace-outer + palace-inner (PLAYBOOK §D) |
host tsconfig.json |
assets/patches/next.rewrites.snippet.ts |
/palace + /palace/os rewrites |
host next.config.ts |
assets/patches/vercel.headers.snippet.json |
Per-path COOP + cache headers (PLAYBOOK §H §I §J) | host vercel.json |
assets/patches/font-and-li.snippet.css |
Self-hosted Alfa Slab One + <li> typography (PLAYBOOK §M §N) |
palace-inner/src/index.css |
assets/patches/monitor-iframe-src.snippet.ts |
Iframe src = '/palace/os?cb=' + Date.now() (PLAYBOOK §I §J §K) |
palace-outer/src/Application/World/MonitorScreen.ts |
assets/patches/sources.ts.template.ts |
Full replacement with /palace/ prefix on every runtime asset path |
palace-outer/src/Application/sources.ts |
assets/patches/palace-outer.package.json.example.md |
Reference: move build-time devDeps → deps (PLAYBOOK §B) | palace-outer/package.json (manual edit) |
assets/content/Home.tsx etc. |
Placeholder showcase pages | palace-inner/src/components/showcase/ |
assets/content/VerticalNavbar.tsx |
Placeholder showcase navbar with PLAYBOOK §L fix (react-router-dom everywhere) |
palace-inner/src/components/showcase/VerticalNavbar.tsx |
assets/content/ShutdownSequence.tsx |
Minimal "Rebooting…" replacement | palace-inner/src/components/os/ShutdownSequence.tsx |
assets/content/branding/*.md (6 files) |
Before/after examples for every Henry-specific branding string | LoadingScreen / InfoOverlay / Toolbar / ShowcaseExplorer / MonitorScreen / Desktop |
assets/content/public/index.html.template.html + manifest.json.template.json |
Inner-OS public templates | palace-inner/public/ |
assets/content/personal-assets-checklist.md |
Walk-through for replacing Henry's photos / resume / favicon | image / PDF / icon files |
assets/fonts/AlfaSlabOne-Regular.woff2 + AlfaSlabOne-OFL.txt |
Self-hosted display font (OFL) | palace-inner/src/assets/fonts/ |
assets/notice/NOTICE.outer.template.md |
MIT vendor notice | palace-outer/NOTICE.md |
assets/notice/NOTICE.inner.template.md |
Permission notice | palace-inner/NOTICE.md |
assets/verification/devtools.js |
Post-deploy verification | paste into DevTools at /palace |
assets/reference/README.md |
What /palace should look like + live demo URL |
— |
assets/reference/audio-cleanup.md |
Silence Henry's ambient office.mp3 + drop / replace inner music tracks |
— |
assets/reference/baked-texture-replacement.md |
Three options for the "Heffernan henry inc" debossed CRT chassis | palace-outer/static/models/Computer/baked_computer.jpg |
The full per-file rationale lives in assets/INDEX.md.
Gitignore
/palace-outer/node_modules
/palace-inner/node_modules
/palace-inner/build
/public/palace
Force-add palace-outer/package-lock.json despite upstream's .gitignore exclusion (PLAYBOOK §C — lockfile pins framer-motion subversions and is the single biggest preventer of Vercel-only build failures).
The 13 gotchas — read PLAYBOOK.md
Every one of these cost hours on the bingranyou.com rollout:
A. License confusion (inner site has no LICENSE) · B. Vercel build-time devDeps stripped · C. framer-motion type drift on fresh install · D. csstype mismatch in Next.js TS check · E. CRA's TS walks up to host node_modules · F. sitemap.ts references deleted files · G. Vercel single-file size & dead asset · H. X-Frame-Options: DENY blocks own-iframe · I. COOP same-origin breaks same-origin iframe access · J. Stale HTTP cache survives header changes · K. Vercel 308-redirect with text/plain breaks iframe · L. react-router context split · M. Typekit + Google Fonts unreliable · N. Bullet text falls back to system sans
Read PLAYBOOK.md for symptom → cause → fix on each.
License & attribution
This skill is MIT. The vendored apps carry their own licenses — see "Prerequisites" above. Keep these visible in your own copy:
palace-outer/LICENSE.md(Henry Heffernan, MIT)palace-outer/NOTICE.md(your modifications + upstream URL)palace-inner/NOTICE.md(your modifications + the permission you obtained + upstream URL)- A visible "template by Henry Heffernan (MIT)" footer in your inner site's window chrome (we use
bottomLeftTexton<Window>inShowcaseExplorer.tsx).
Worked example
The rollout that produced this skill — bingranyou.com:
- Initial vendor + content swap: bingran-you/bingran-you#187
- Font fix saga: #189 → #191
- Iframe-on-prod saga: #194 → #195 → #196 → #197 → #198
Walk those PRs in order to watch each gotcha play out and how it was caught and fixed.