Skip to main content
This page was machine-translated and may contain errors or omissions.

Canvas Budgets and Word Clouds

Images are compression budgets; word clouds count characters

Sat Feb 07 2026
3364 words · 17 minutes

Canvas Budgets and Word-Cloud Lies

Images get displayed and shared only because of compression, and where the cost of that compression lands is where each image style grows. Another batch of tools, filed under creative, produces no content at all — only structure — and when that structure is computed wrong, as when a Chinese word cloud passes character frequency off as word frequency, the prettier the picture the more it misleads. This post runs the two threads together: the first half is about how a canvas budget gets spent, the second about how a tool’s structure can lie.

The Compression Budget Behind Image Styles

A 4000×3000 photo holds 12 million pixels at 3 bytes each, 36MB in all, and that it can be displayed and shared at all comes down to compression. Pixel art, 8-bit palettes, GIF, ASCII art — lumped together as retro, they are technically variants of one gesture: pick a constraint on purpose, then find the optimum inside it.

The entry points are scattered across Pixel Art Editor, ASCII Art, GIF Maker, Image Tool, NES Palette, Color Tool, and Visualizer.

How Much Information Is in an Image

A bitmap carries W×H×24W \times H \times 24 bits, 8 bits per RGB channel. Compression has exactly three routes, and they map one-to-one onto three aesthetics.

Spatial redundancy. Neighboring pixels tend to be similar, so difference coding plus entropy coding squeezes them out. PNG takes this route: lossless, stylistically neutral. Color count. Cutting 16 million colors down to 256, 16, or 4 loses the gradients and gains a sense of flat planes. Frequency-domain loss. JPEG simply throws away detail the eye is insensitive to, chroma especially, losing sharpness and gaining lightness.

Pixel art deliberately takes the second route plus the first. It does not aim to look like a photo; it aims to stay legible at an extremely low color count. That goal, in turn, dictates the drawing method.

Pixel Art: The Grid Is the Coordinate System

The Pixel Art Editor keeps a single two-dimensional color array as its core state, offers a canvas size, and exports a PNG at the current grid size. One implementation detail says a lot: the display pixel size is computed as max⁡(4,min⁡(16,⌊512/n⌋))\max(4, \min(16, \lfloor 512 / n \rfloor)). The denser the grid, the smaller each cell, yet the canvas stays almost constant in physical size on screen.

That keeps an 8×8 piece and a 64×64 piece looking the same size. You judge the composition, not the canvas dimensions.

The real constraint sits here: at low resolution there is no such thing as a half pixel. So every higher-order technique degrades into lightness control.

Lightness steps: don’t separate forms by hue, separate them by three or four lightness levels, because hue is simply unreadable within 16px. Outlines: don’t use pure black for the outer contour, use a darker version of the body color, so the outline blends in instead of looking dirty. Anti-aliasing is a luxury: smooth edges need extra colors, which a 16-color palette cannot spare, so the jaggies themselves become part of the style.

In other words, the training value of pixel art is not a steady hand but the practice of trade-offs. You have to decide before you start how many levels of information this canvas can carry at most.

NES: Where 25 Colors per Screen Comes From

The NES Palette lists the master palette of the 2C02 PPU: 64 indexed entries, 55 distinct colors after deduplication, one of which is blacker than black and practically invisible over composite video.

Many people take that to mean the NES can display 55 colors. Not remotely.

The hardware’s real limit runs like this. The whole screen picks exactly 1 global background color. The background has 4 palettes of 3 colors each, 13 counting the global color. Sprites have 4 palettes of 3 colors each, 12 counting transparency.

So one screen tops out at 1+12+12=251 + 12 + 12 = 25 colors, with only 3 free slots per palette. That is the technical core of the NES look — not muted greens, reds, and blues, but an extreme scarcity of palette slots.

Understand that and two phenomena explain themselves. Why a palette swap is the standard way to make a new enemy in NES games: the same sprite palette is reused and only the indices change. Why the water, sky, and ground of a background must share those 3 colors: one palette per screen.

The art style is the hardware budget made visible, not an aesthetic choice.

Game Boy: Four Shades, No Backlight

The Game Boy Filter goes further than the NES. Every pixel of an arbitrary image is mapped by luminance onto one of the four green-grey shades of the DMG panel: nearest-neighbor quantization — compute luminance, snap to the closest of the four shades, and never dither.

Skipping dithering has a blunt consequence: continuous gradients are cut into a few hard bands and faces become flat slabs. That was exactly the situation in 1989 — 160×144, 2-bit depth, no backlight, ambient reflection only, response time in the hundreds of milliseconds. Artists of the day could only subtract within those four shades: lightness steps for depth, one-pixel highlights to suggest material, outlines to patch up shape. CRT scanlines and phosphor afterglow are display defects of a different order; this site’s take on them is in the retro signal post.

GIF: 256 Colors, LZW, and the 216-Color Cube

The GIF Maker strings several images into an animation, which looks like plain looping but actually runs into the oldest engineering in image coding: indexed color.

GIF has only two format constraints, both hard. At most 256 colors per frame, and LZW for the encoding. This site’s implementation is a hand-written GIF89a encoder, and the pipeline shows three steps clearly.

Build a palette — no adaptive quantization like median cut, just a uniform 63=2166^3 = 216-color cube, six levels of 0, 51, 102, 153, 204, 255 per channel. Quantize per pixel: find the nearest cell of the 216 by Euclidean distance and write its index. Then LZW encoding plus frame delays, where the delay field is in hundredths of a second, so 20ms is written as 2.

Each of those three choices has a price.

A uniform cube is simple and predictable but brutal on gradients: low-contrast regions like sky and skin band visibly. Adaptive quantization concentrates colors where they actually appear — far better results, far more computation.

Per-pixel nearest-neighbor search is O(pixel count×216)O(\text{pixel count} \times 216) — an 800×600 image means a hundred million distance computations — so in practice you must prebuild a lookup table, compressing each RGB channel to 6 levels and indexing directly.

The hundredth-of-a-second delay unit means GIF’s time resolution is inherently 10ms, so any 16.7ms frame timing rounds to 20ms. That explains why a slightly wrong playback speed is the norm in GIF rather than a bug.

One more ledger entry you can’t dodge. Inter-frame compression in GIF is weak — early on the only trick was storing just the changed rectangle — so file size is roughly proportional to frame count times frame area. To shrink a GIF, cutting area beats cutting frames. Which is also why reaction images are always a small squarish patch.

Symmetry Groups: Kaleidoscopes and Their Approximation

The Visualizer’s effect looks like mathematical magic: one careless stroke and an intricate radial pattern grows on screen. The implementation is minimal — rotate the brush position about the canvas center by k⋅45°k \cdot 45° for k=0..7k = 0..7 and draw once per rotation, and eight copies assemble themselves into a symmetric image.

But there is an exact distinction that is easy to miss. This is 8-fold rotational symmetry, the cyclic group C8C_8. A real kaleidoscope has 8 mirrors, the dihedral group D8D_8. The difference is nothing but the presence of mirror reflections.

You can verify that difference by eye. Hold a rotation-only pattern up to a mirror and it will not coincide with itself; a real kaleidoscope pattern will. So the more faithful approach applies the 8 rotations and their mirrors together, at the cost of doubling the drawing per stroke. That also explains a common impression: many web kaleidoscopes look too tidy, not shattered enough, precisely because the mirror half of the operation is missing.

The upside of a symmetry group is that it compresses. D8D_8 has 16 symmetry operations, so in theory you need store only 1/16 of the image and derive the rest. Wallpaper, texture, and ornamental pattern design all use this trick.

Contrast: Readability You Can Compute

The least fun and most underused part of the Color Tool is the contrast check. Its formula is fully determined, with no subjective component.

Step one, convert sRGB values to relative luminance. Undo gamma first: c/12.92c/12.92 when c≤0.03928c \le 0.03928, otherwise ((c+0.055)/1.055)2.4((c+0.055)/1.055)^{2.4}; then weight by the Rec.709 coefficients.

L=0.2126 R+0.7152 G+0.0722 BL = 0.2126\,R + 0.7152\,G + 0.0722\,B

Step two, contrast is the ratio of the two luminances.

ratio=Llighter+0.05Ldarker+0.05\text{ratio} = \frac{L_{\text{lighter}} + 0.05}{L_{\text{darker}} + 0.05}

The WCAG thresholds are 4.5:1 for AA body text and 7:1 for AAA, dropping to 3:1 for large text — 18.66px bold or 24px. Those numbers work as a standard because they correspond to measurable reading performance, not designer taste.

The most common trap lies here too. Contrast is a luminance ratio, not a hue difference. Green text on red looks wildly different in color, but the two relative luminances may differ by only 1.5×, and to a colorblind user they are nearly the same gray. Red-green color vision deficiency affects roughly 8% of men. The tool’s color-vision simulation implements this with color matrix transforms, which is far more reliable than “I think it reads fine”.

ASCII Art: Two Completely Different Principles

ASCII Art implements bitmap glyphs. It ships a hand-written 5×5 character bitmap table, plus a larger 10×6 version, looks up the input text character by character, and assembles a matrix of characters row by row. The output is letters drawn out of characters, each letter shape designed by hand.

That is entirely different from the other technique sharing the name, luminance ramp mapping: downsample the image and replace each block’s brightness with a character from a density-ordered ramp like " .:-=+*#%@". The first preserves form, where shape matters; the second preserves tone, where light and dark matter.

Why the distinction matters: the first can only draw a limited character set — hand-designing every cell is too expensive — so it covers A-Z, 0-9, and a few symbols. The second can draw any image but cannot render readable text at all. Many ASCII tools in practice mix both, ramps for the picture and bitmaps for the heading.

Look at an ASCII tool’s output and you can guess which principle it uses.

Limits Are the Budget

The first decision when making an image usually has nothing to do with style; it is deciding what to give up. Next time you open the Pixel Art Editor, fix a color limit first — 8, say — and then start.

On the image side the constraints are priced and printed right into the parameters; the constraints of the tools below are buried in the algorithm, invisible until you read the implementation.

Creative Tools Produce Structure, Not Content

A word cloud hands you an attention map, placeholder text and placeholder images hand you a shell you can measure, and a character generator hands you a starting point you don’t have to build from zero — filed under creative, these tools produce no content at all, only structure. Expect the tool to give you an answer and you will be disappointed; use them to lay the foundation first and your throughput doubles.

The tools live at Word Cloud Generator, Lorem Ipsum, Lorem Image, and RPG Character Generator.

Tokenization Granularity Decides Everything

Word Cloud Generator runs in three steps: tokenize, count, lay out. The first two look like grunt work, and the whole picture’s credibility rests on them.

The implementation here splits text with one regex. Every character that is neither a Han character nor alphanumeric becomes a space, then the string is split on whitespace. Each chunk is split again — Chinese by single character, English by whole word, minimum length 2, all lowercased. Finally the counts are tallied and the top 100 by descending frequency are kept.

This splitting has one consequence you have to know about. A Chinese word cloud counts character frequency, not word frequency.

So function words like 的, 是, 不 and 了 top the list without suspense, while 人工智能 gets split into four separate entries — 人, 工, 智, 能 — each counted on its own. The picture looks exactly like a word cloud; the conclusion is mostly noise.

Doing it right takes two more passes.

One is Chinese word segmentation: use a dictionary or a statistical model to recognize 人工智能 as a single word. A pure front-end tool with no segmenter can’t do this and falls back to character frequency. The other is a stop-word list, filtering out high-frequency function words like 的, 了, 是, 在 and 和. English needs the same treatment plus lemmatization — running, runs and ran all fold into run, otherwise one concept is split three ways and its frequency gets diluted.

So the first question to ask of a Chinese word cloud is not “isn’t this pretty” but “did it segment”. A word cloud without segmentation is decoration at best.

Layout, an NP-Hard Problem Solved Greedily

After counting comes typesetting, and typesetting is rectangle packing: cram N rectangles of different sizes into one canvas without overlap. The problem is NP-hard and exact solutions are out of reach, so every word cloud library uses the same family of greedy approximations.

Font size maps linearly to frequency, f=fmin⁡+c−cmin⁡cmax⁡−cmin⁡(fmax⁡−fmin⁡)f = f_{\min} + \frac{c - c_{\min}}{c_{\max} - c_{\min}}(f_{\max} - f_{\min}), and the site takes fmin⁡=14f_{\min} = 14 and fmax⁡=min⁡(60,canvas width/6)f_{\max} = \min(60, \text{canvas width}/6). Placement starts from the canvas center and tries positions along an Archimedean spiral in steps of 0.3 rad in angle and 1.5 px in radius. Every candidate is hit-tested against the bounding box of everything already placed, with 4 px of padding. The first non-conflicting spot takes the word; otherwise the spiral keeps going.

Three engineering facts here deserve to be taken separately.

Center-first equals importance-first. The spiral runs outward from the center, so high-frequency words naturally occupy the visual center. That’s a by-product of the design, not the algorithm’s target.

If it doesn’t fit, it’s dropped. Each word gets at most 800 placement attempts; if nothing lands, it is silently discarded. Which is to say a word cloud never shows all the words. You think you’re seeing the whole picture; you’re seeing the part that managed to squeeze in.

Bounding boxes are an approximation. A glyph’s actual ink isn’t a rectangle — 一 and 國 differ hugely in ink coverage — so rectangular hit-testing leaves ugly wide gaps. Serious typesetting uses glyph outlines as polygons for collision, at a cost of one to two orders of magnitude in speed.

Area Encoding, Which the Eye Cannot Judge

Even with perfect segmentation and a tight layout, a word cloud has a fundamental problem. It encodes values as area, and the human eye estimates area badly.

Comparing lengths, the eye errs within about 10%; comparing areas, the error climbs past 30%. Cleveland and McGill established this systematically back in the 1980s, and it is the empirical basis for bars beating pies and pies beating bubbles. Word clouds are worse still, because the area also varies with character width. A 40px 我 and a 40px W cover completely different amounts of ink, and there is no comparing them.

So a word cloud’s role should be stated flatly. It is an attention-distribution sketch, not a data chart. It answers which concepts keep recurring; it does not answer how many times more one appears than another. For precise comparison, sort those words into a bar chart — that’s the form you can read magnitudes off of. Of this batch, only Word Cloud Generator’s output gets passed around as a conclusion, and it is precisely the visual encoding most prone to distortion — fine for finding leads, never for reaching verdicts; if you want to know how many times a word appeared, count.

Its legitimate use lies elsewhere. Dump a year of diary entries in and see which words keep surfacing; that’s self-observation, not data analysis. The picture being ugly doesn’t matter. The word you catch sight of yourself is the payoff.

Why Placeholder Text Must Be Meaningless

Lorem Ipsum generates that famous Latin passage, Lorem ipsum dolor sit amet. It’s often treated as filler that looks sophisticated; the actual design intent is the opposite — it has to be semantically empty.

The reasoning is simple. Designing a page means doing two things at once, fixing the layout and writing the content, and the two compete for the same attention. If the placeholder text is readable Chinese, review attention inevitably slides toward whether the sentence is well written rather than whether the column width is right. A stretch of Latin nobody can read is a label saying “no information here”, forcing attention back onto the typography itself.

It has a second job: measuring. The basic parameter of typesetting is the measure, and the rule of thumb for Latin text is 45 to 75 characters per line — Bringhurst’s recommendation; go over and lines break badly, go under and the text fragments. Placeholder copy provides a stable character density, so you can tune column width, leading and type scale with no real content in hand, and the layout won’t collapse when the real thing arrives.

But placeholder copy hides a real risk. Real content has a completely different length distribution. Placeholder text has no overlong URL, no unbroken English word, no C++ template symbols, no mixed CJK. Plenty of layouts tuned on lorem ipsum get blown apart by one long link the moment real content lands.

So the professional move is to test with near-real extremes: the longest heading, the longest unbroken string, the shortest paragraph.

Placeholder Images, Locking Ratios Against Shift

Lorem Image returns an image at a given size, and its engineering value runs well past the word placeholder — it pins down the aspect ratio.

The most common image problem on the web isn’t slow loading, it’s layout shift during loading. With no height reserved, the image shoves everything below it down the instant it finishes, and the line the reader was on suddenly bolts. There is exactly one fix: claim the space it will finally occupy before it loads, i.e. declare the aspect ratio explicitly.

The second use is ratio verification. Is the 16:9 in the design file the same 16:9 as in the code. Drop in a 1600×900 placeholder and cropping and letterboxing are visible at a glance. That’s more reliable than grabbing some random image and parking it there, because a random image drags its own composition in and hides the ratio problem.

Random Characters, Where Distribution Beats Range

RPG Character Generator looks like the most toy-like of the four, yet it is the only one that touches generation quality.

Attributes use 4d6 drop lowest. Roll four six-sided dice, discard the smallest, sum the rest. The range is 3 to 18, but the distribution isn’t uniform — it’s a bell skewed right. Which means high attributes are common good luck, and all 18s stay rare. Same as D&D’s classic method. Roll randInt(3, 18) uniformly instead and characters feel flat and strange: no conspicuous strength, no fatal weakness.

Names concatenate a prefix, an infix and a suffix, then collapse consecutive repeated letters once. The combination count is only in the hundreds to low thousands, yet they genuinely read like names, because the syllable structure is constrained by the template.

Race, class and the six stats leave plenty of combination space — 10 races times 12 classes — enough that no two generations come out alike.

All three rest on one principle: the quality of random generation depends not on the value range but on the shape of the distribution. Uniform sampling is the easiest thing to write and the worst. It makes every outcome equiprobable, so there is neither a mediocre majority nor a boast-worthy few, and the generator loses all narrative tension.

The principle transfers to every generative tool. Map generation has to control the octave stacking of noise, drop systems have to control rarity weights, text generation has to control the sharpness of temperature. Any time you produce content at random, the first question is what shape you want the distribution to have.


Thanks for reading! Follow me if you'd like~

Canvas Budgets and Word Clouds

Sat Feb 07 2026
3364 words · 17 minutes