Here’s a meta post for you, if there ever was one: if someone shared this post with you and a preview image showed up, that image was generated by the thing this post describes. The post is its own demo. I find that unreasonably satisfying.

It looks like this, generated live from the post’s own title and description as you read this:

This page's og:image, rendered by codimg from its own metadata

(If I change the title, the image changes with it. There is no image file.)

Every post on this blog has been sharing the same static banner.jpg since I set the thing up (not because I picked it with care, mind you, but because that’s what happens when you configure a Hugo theme once and never look at the og:image tag again). Meanwhile, Proper™ blogs have those per-post preview cards with the title in big friendly letters. I wanted that. What I did not want was a build step generating and storing hundreds of PNGs.

Then I remembered codimg, the little Go (+ Elm for the playground, and because I couldn’t help myself) service from a couple of months back that turns code into syntax highlighted SVG. Completely stateless; the code lives inside the image URL. So a per-post preview image is just a URL, and Hugo can put URLs together at build time just fine.

So what does a blog post look like as code? Like this, obviously:

{- cekrem.github.io/posts/[slug]
-}

title : String
title = "[title]"

description : String
description =
    "[description, wrapped at 56 characters and joined "
        ++ "with (++) like a civilized person would]"

(With an ASCII art banner on top, naturally.)

A Hugo partial builds that snippet for every post, URL-encodes it with querify, and drops the codimg URL straight into og:image and twitter:image. When a crawler asks for the image, codimg renders it on the spot.

A couple of confessions. codimg accepts deflate-compressed base64 input to keep URLs short, but Hugo can’t deflate anything at template time, so I’m sending plain URL-encoded Elm. Roughly 2KB per URL. It’s fine. Also: I wrote a word-wrapper in Hugo’s Go templates to break long descriptions into that ++ chain. I’m not proud of it. (I’m a little proud of it.)

So will it actually work? On some platforms it will! Though a few platforms simply disregard banners in the svg format, it’s honestly better with their fallback text + description in plain text than that generic blog banner.

And it was fun 🤓