How My Blog Posts Generate Their Own Images

Written by shinobis-com | Published 2026/03/06
Tech Story Tags: generative-art | web-development | design | web-design | php | blogging | ai | generative-blog-images

TLDRI built a system where blog posts create their own visual identity from content hashes using PHP and SVG. The result is deterministic: same content, same image, every time. via the TL;DR App

If you have browsed my blog you may have noticed that each article has a different square image. A pattern of cubes that resembles a QR code but is not one. Colors that change from one post to another. And in the center, always the same kanji: 忍.

Those images were not manually designed. They were generated by the content itself of each article.

How it works

When I publish an article the system takes two pieces of data: the title and the character count of the content. From the title it generates a cryptographic hash, a unique and unrepeatable string of characters. That hash becomes a map that decides which cubes in a 32 by 32 grid get filled and which remain empty. It is the same principle a real QR code uses but without encoding any readable data. Just the DNA of the text converted into visual form.

The character count determines the accent color of each image:

Short articles are gold. Medium-length ones shift through cool blue and purple. Longer ones are terracotta. And if I ever write something truly extensive, it will be mint green.

The result is that each article has a unique visual fingerprint. If I change a single word in the title the pattern changes completely. If the article grows or shrinks past a threshold the color mutates. The image is alive as long as the text is.

The kanji in the center

At the center of each image there is a white space with the kanji 忍. It reads shinobi in Japanese. It means to persevere in silence, to endure with patience, to move unseen. It is the origin of this blog's name and the visual anchor connecting all images to each other regardless of how different their patterns are.

Why not use regular images

I could have used stock photography or Midjourney-generated images for each post. But this blog talks about the intersection of design and technology. I wanted even the images to reflect that philosophy. Not decoration but data converted into form. Each one telling something about the article it represents without needing to read it.

There is also a practical reason. This system is fully automatic. When I publish a new article the images for all three languages generate themselves. I do not need to search for photos, open Photoshop, or think about which image to use. The content illustrates itself.

Technical details

The system runs on PHP with the GD library. No external APIs, no JavaScript, no build step. When a post is published, the image generates itself in milliseconds. Step 1: Extract the DNA Every image starts with two inputs: the post title and the character count of the content.

$charCount = mb_strlen(strip_tags($content), 'UTF-8');
$titleHash = md5($title . $postId);
$contentHash = sha1($content . $postId);

The title hash drives the visual pattern. The content hash adds entropy. The character count determines color. Three inputs, three layers of identity. Step 2: Map characters to color The character count places each article on a color spectrum. Short articles get warm gold. Longer ones shift through blue, purple, and terracotta. Anything above 9000 characters becomes mint green.

$colors = [
    [200, 170, 100],  // gold (under 3000 chars)
    [100, 170, 200],  // cool blue (3000-5000)
    [170, 100, 200],  // purple (5000-7000)
    [200, 120, 100],  // terracotta (7000-9000)
    [100, 200, 150],  // mint green (9000+)
];

$colorIndex = min(4, intdiv($charCount, 2000));
$accent = $colors[$colorIndex];

This means you can guess the length of an article just by looking at its image. Color is data. Step 3: Expand the hash into a grid A single MD5 hash is 32 characters. The grid is 32x32 = 1024 cells. To cover every cell, the system chains multiple hashes together:

$fullHash = $titleHash . $contentHash . md5($charCount . $postId);
$expandedHash = '';
for ($i = 0; $i < 20; $i++) {
    $expandedHash .= md5($fullHash . $i);
}

This produces 640 hexadecimal characters, more than enough to map every cell in the grid. Each hex character (0-F, value 0-15) determines what happens to that cell. Step 4: Fill the grid Each cell reads one character from the expanded hash and decides its fate:

for ($row = 0; $row < 32; $row++) {
    for ($col = 0; $col < 32; $col++) {
        $hashChar = $expandedHash[$hashIndex % strlen($expandedHash)];
        $hashVal = hexdec($hashChar); // 0-15
        $hashIndex++;

        if ($hashVal >= 10) {
            // Dark cube (values A-F)
            $brightness = 20 + ($hashVal - 10) * 8;
            // Fill with near-black
        } elseif ($hashVal >= 7) {
            // Accent-colored cube (values 7-9)
            // Fill with the content-length color
        } elseif ($hashVal >= 5) {
            // Light gray cube (values 5-6)
        }
        // Values 0-4: empty (white)
    }
}

The distribution is roughly: 37% dark cubes, 19% accent cubes, 13% gray cubes, 31% empty. This ratio produces patterns dense enough to feel like data but sparse enough to be readable. Step 5: Clear the center A 6x6 cell window at the center of the grid is wiped clean. This creates visual breathing room and makes space for the kanji logo:

$centerStart = (32 / 2) - 3;
$centerEnd = (32 / 2) + 3;

for ($row = $centerStart; $row < $centerEnd; $row++) {
    for ($col = $centerStart; $col < $centerEnd; $col++) {
        imagefilledrectangle($img, $x1, $y1, $x2, $y2, $whiteBg);
    }
}

The kanji 忍 (shinobi, to endure in silence) is rendered using the Noto Sans CJK font if available on the server, tinted with the same accent color. Step 6: Save and serve The final image is a 1200x1200 PNG at compression level 7. Each post generates one image per language, stored as post-{id}-{lang}.png:

$filepath = "uploads/social/post-{$postId}-{$langCode}.png";
imagepng($img, $filepath, 7);

The same image is reused in three contexts: as a thumbnail on the homepage, as a header inside the article, and as the Open Graph image when shared on social media. The result The system is deterministic. Same content, same image, every time. No randomness, no external dependencies. Change one word in the title and the entire pattern shifts. Add a paragraph and the color might change. It is generative design in the most literal sense: the content generates its own identity. Try it yourself The complete source is a single PHP file under 250 lines. It needs PHP with GD enabled and nothing else. The core logic, hash expansion, grid mapping, and color selection, can be adapted to any language. The principle is simple: take any unique string, expand it into enough entropy to fill a grid, and map value ranges to visual properties. If you are a developer or designer and want to implement something similar, the approach works for any content platform where you want unique, automatic, and deterministic visuals without relying on AI generation or stock photography.


Written by shinobis-com | UX/UI Designer · 10+ years in banking & fintech · Building with AI from the trenches, not the sidelines · shinobis.com
Published by HackerNoon on 2026/03/06