GET /api/work/portfolio-site
This Portfolio
Live · · solo
This website. It has two voices, plain English and engineer, and a whole second mode where the portfolio becomes a help desk. Also it can run games.A content site that can also run games, built so the writing stays easy and the demos stay cheap.
- Astro
- TypeScript
- Tailwind
- Cloudflare Workers
- MDX
- JS on a write-up
- 0 KB
- Hosting
- $0/mo
- Demo lanes
- 2
Parameters
Request
curl "https://portfolio.dylansg0318.workers.dev/api/ work/ portfolio-site"
Response200 OK · 2.3 KB
{ "id": "portfolio-site", "title": "This Portfolio", "status": "live", "year": 2026, "category": "tool", "role": "solo", "summary": "A content site that can also run games, built so the writing stays easy and the demos stay cheap.", "summary_plain": "This website. It has two voices, plain English and engineer, and a whole second mode where the portfolio becomes a help desk. Also it can run games.", "stack": [ "Astro", "TypeScript", "Tailwind", "Cloudflare Workers", "MDX" ], "metrics": { "js_on_a_write_up": "0 KB", "hosting": "$0/mo", "demo_lanes": "2" }, "links": { "source": "https://github.com/Dylansg318/Portfolio" }, "problem": "A portfolio has two jobs that pull in opposite directions. It has to be a fast, readable, mostly-text site, and it also has to run interactive things like games, which are the heaviest thing a browser can be asked to do. Optimise for one and you compromise the other.", "unique": "Rather than pick a side, the interactive work is isolated behind a seam with two lanes: demos written in-repo hydrate as islands, and prebuilt engine exports run in a sandboxed iframe that never enters the site's build graph. Both are declared in one frontmatter field, and neither costs a reader anything until they click.", "ai": "Scaffolded with Claude Code from a written brief: the two-lane demo seam, the required narrative fields, and the contrast gate were my decisions, made before any code existed. The agent produced the first pass; I cut what it over-built (a React runtime for a two-state theme toggle, for one) and kept the build honest by making the write-up fields fail the build when missing.", "learned": [ "Making the write-up fields required in the schema, not just conventional, turns \"explain what you learned\" from a habit into a build error. The tooling does the remembering.", "The cheapest performance win isn't optimising the heavy thing, it's not loading it. Click-to-start on every demo and video removes the cost entirely for the majority of visitors who only came to read.", "Committing to design tokens before designing anything means the eventual visual pass is one file, not an audit of every component." ], "page": "https://portfolio.dylansg0318.workers.dev/projects/portfolio-site" }
Live: this is the answer from this site’s API, not a mock. Send re-runs the request with your parameters; if the answer names a page, it opens.
The problem
A portfolio has two jobs that pull in opposite directions. It has to be a fast, readable, mostly-text site, and it also has to run interactive things like games, which are the heaviest thing a browser can be asked to do. Optimise for one and you compromise the other.
What was unique
Rather than pick a side, the interactive work is isolated behind a seam with two lanes: demos written in-repo hydrate as islands, and prebuilt engine exports run in a sandboxed iframe that never enters the site's build graph. Both are declared in one frontmatter field, and neither costs a reader anything until they click.
Where AI fit in
Scaffolded with Claude Code from a written brief: the two-lane demo seam, the required narrative fields, and the contrast gate were my decisions, made before any code existed. The agent produced the first pass; I cut what it over-built (a React runtime for a two-state theme toggle, for one) and kept the build honest by making the write-up fields fail the build when missing.
Parameters
Request
curl "https://portfolio.dylansg0318.workers.dev/api/ work/ portfolio-site? fields=problem%2Cunique%2Cai"
Response200 OK · 1.1 KB
{ "problem": "A portfolio has two jobs that pull in opposite directions. It has to be a fast, readable, mostly-text site, and it also has to run interactive things like games, which are the heaviest thing a browser can be asked to do. Optimise for one and you compromise the other.", "unique": "Rather than pick a side, the interactive work is isolated behind a seam with two lanes: demos written in-repo hydrate as islands, and prebuilt engine exports run in a sandboxed iframe that never enters the site's build graph. Both are declared in one frontmatter field, and neither costs a reader anything until they click.", "ai": "Scaffolded with Claude Code from a written brief: the two-lane demo seam, the required narrative fields, and the contrast gate were my decisions, made before any code existed. The agent produced the first pass; I cut what it over-built (a React runtime for a two-state theme toggle, for one) and kept the build honest by making the write-up fields fail the build when missing.", "page": "https://portfolio.dylansg0318.workers.dev/projects/portfolio-site" }
Live: this is the answer from this site’s API, not a mock. Send re-runs the request with your parameters; if the answer names a page, it opens.
This page is the template. Everything below shows a component you can use in your own write-ups; delete the prose, keep the shapes.
The demo seam
The frontmatter above declares demo.kind: island, which mounts a component from
src/demos/galaxy-defense/. It’s a real game, a Code.org project of mine ported to
canvas, and it’s the same island the Galaxy Defense
write-up mounts. One folder, two pages:
The important part isn’t the game, it’s that switching this to a Godot export is a frontmatter change:
demo: kind: iframe src: /demos/my-game/index.html aspect: "16 / 9"No component changes, no build config. A 40MB WASM bundle sits in public/ and gets a
sandboxed iframe injected on click. The seam exists so the second game costs the same
as the first.
Code that can’t go stale
<CodeFile> reads a real file off disk at build time and highlights it. The snippet
below isn’t a copy, it’s the file:
// The narrative contract, lifted from src/content.config.ts.// These three fields are what make every project page answer the same questions.
const contract = z.object({ /** What was actually wrong or needed. Not "I built an X". */ problem: z.string(),
/** What was non-obvious here — the judgement call, constraint, or trick. */ unique: z.string(),
/** Concrete takeaways. At least one, or the build fails. */ learned: z.array(z.string()).min(1),});
// Because these are required rather than conventional, a write-up that skips// the thinking cannot be published. The build is the editor.Pasted code drifts from the code it describes. This can’t, because there’s only one copy. If the file moves or gets deleted, the build fails instead of quietly showing something that used to be true.
Video
Video is never committed here; GitHub rejects files over 100MB and Cloudflare caps a
static asset at 25MiB, so a three-minute screen capture breaks both. <Video> takes a
provider and an id, renders a poster, and only injects the real player on click:
<Video provider="youtube" id="dQw4w9WgXcQ" title="Walkthrough of the order pipeline" />Three videos on a page cost nothing until one is watched. Swapping YouTube for Bunny or Cloudflare Stream later is a prop change.
What the reader always gets
Every project renders The problem and What was unique above the write-up and What I learned below it, in the same places every time. A reader comparing two projects finds the same answers in the same spots, which is what makes a portfolio scannable instead of a pile of posts.
What I learned
- Making the write-up fields required in the schema, not just conventional, turns "explain what you learned" from a habit into a build error. The tooling does the remembering.
- The cheapest performance win isn't optimising the heavy thing, it's not loading it. Click-to-start on every demo and video removes the cost entirely for the majority of visitors who only came to read.
- Committing to design tokens before designing anything means the eventual visual pass is one file, not an audit of every component.
Parameters
Request
curl "https://portfolio.dylansg0318.workers.dev/api/ work/ portfolio-site? fields=learned"
Response200 OK · 629 B
{ "learned": [ "Making the write-up fields required in the schema, not just conventional, turns \"explain what you learned\" from a habit into a build error. The tooling does the remembering.", "The cheapest performance win isn't optimising the heavy thing, it's not loading it. Click-to-start on every demo and video removes the cost entirely for the majority of visitors who only came to read.", "Committing to design tokens before designing anything means the eventual visual pass is one file, not an audit of every component." ], "page": "https://portfolio.dylansg0318.workers.dev/projects/portfolio-site" }
Live: this is the answer from this site’s API, not a mock. Send re-runs the request with your parameters; if the answer names a page, it opens.