Silicon

Introducing naman.md

A markdown-first blog with shader-rendered artifacts, built on Next.js — and why it's just a folder of files in a git repo.

Today I'm launching naman.md — a plain-markdown blog with a deliberately technical aesthetic: monospace labels, a strict grayscale palette, and one generative shader blob per post, seeded by the post's slug so it never renders the same way twice across posts.

Files over apps

Every few years I export my writing out of some platform's database and swear it's the last time. This site is the last time.

A blog should outlive its stack. Every post on this site is a single .md file with YAML frontmatter, committed to git. Publishing is git push. There is no draft state that isn't a branch, no edit history that isn't a commit log, and no backup strategy that isn't already solved by having the repo cloned in three places. No CMS, no database, no admin panel — just files that will still open in a text editor in twenty years.

The best storage format is the one you can cat. Everything else is a migration waiting to happen.

What you give up:

  • A WYSIWYG editor — you write in whatever editor you already live in
  • Scheduled publishing — you push when it's ready
  • Analytics dashboards baked into an admin panel

What you get back:

  • Portability. The content layer has zero dependencies on the rendering layer.
  • Reviewability. A post can get a pull request, comments and all.
  • Longevity. Markdown from 2004 still renders. Database dumps from 2004 mostly don't.

The trade is lopsided once you notice that everything in the first list is a feature for the platform, and everything in the second is a feature for you.

The pipeline

The rendering side is small on purpose:

  1. gray-matter splits frontmatter from the body
  2. remark parses markdown with GFM extensions
  3. rehype renders HTML, with Shiki highlighting code at build time
  4. Next.js statically generates every page — zero client-side markdown parsing

Every route is static: per-post Open Graph images, RSS, sitemap, robots, and JSON-LD all render at build time.

The shader artifact

Each post's hero is a WebGL fragment shader — an organic blob whose outline is displaced by fractal Brownian motion. The slug is hashed to pick three accent colors and a noise offset, so the artifact is deterministic per post but unique across posts.

The core of the outline logic fits in a few lines of GLSL:

shader-blob.frag
vec2 uv = (2.0 * gl_FragCoord.xy - u_resolution) / min(u_resolution.x, u_resolution.y);
float angle = atan(uv.y, uv.x);
 
// Sample noise around the circle to wobble the radius.
vec3 ring = vec3(cos(angle), sin(angle), u_time * 0.12) * 1.4 + u_seed;
float radius = 0.62 + 0.22 * (fbm(ring) - 0.5);
 
float body = smoothstep(0.02, -0.02, length(uv) - radius);

The React side is a single client component. Everything else on the page is server-rendered:

page.tsx
<div className="post__hero">
  <ShaderBlob seed={post.slug} />
</div>

The canvas respects prefers-reduced-motion — if you've asked your OS to calm things down, the blob renders a single static frame instead of animating.

What's next

More posts on systems, tooling, and the occasional shader. Subscribe via RSS — or don't, the files aren't going anywhere.