More from The personal website of Matt Ström-Awn
Four years ago, I wrote “How to pick the least wrong colors.” The gist is: picking a categorical color palette is an optimization problem. There’s no such thing as the right colors. But if you use the right cost function, and the right kind of hill climbing, you can at least get the least wrong ones. Since the original post I’ve been slowly picking away at improvements and new approaches. Now that we’re past the singularity, I’ve put a few coding robots on the job. It’s reassuring that many of my assumptions were good ones! The robots have been able to improve the code, bridging some of the gaps in my own knowledge. Today, I’m publishing an updated version of the algorithm as an npm package, along with a fancy GUI version. While there’s still more to do, I’m proud of how far I’ve been able to take it. What’s new New evaluators More controls The public API and a CLI What’s improved The annealing algorithm Configurable color space and distance metric The results One more thing Acknowledgements What’s new New evaluators Almost as soon as I published the first version, I realized that the cost function lends itself really well to modularity. Beyond my initial evaluation functions, I could design new ones, and provide a framework for anyone to plug in their own. As a recap, my original criteria for good categorical colors, mapped to evaluation functions: Similarity — a way of measuring the similarity of one palette to another, useful for providing art direction and getting brand alignment Energy — the colors should be different from each other so they aren’t liable to be confused from one another Range — the differences between the colors should be consistent so unintended groupings don’t appear Color vision deficiency — simulating the colors under different types of color blindness (red-green, blue-yellow, partial to full tritanopia) Here’s the new evaluators: JND — strongly reject palettes that have two or more colors that are too similar Avoid — the mirror image of the similarity evaluation, push colors away from a user-defined set Contrast — compares colors, keeping them above the WCAG AA color contrast floor. Can be used with a background color to maintain contrast on a chart’s background Saliency — uses color naming study data to prefer colors that are easy to name Name difference — the mirror image of saliency, avoiding colors that share names Each of these evaluators can be weighted, indicating the kinds of tradeoffs and priorities you’d like for your color palette. Additionally, the whole evaluator system is pluggable: you can define your own evaluators and have them drive the optimizer! More controls Colors can now be fixed in place, or pinned to a particular order, making it easier to load in existing palettes and optimize all or just some of the colors. Individual channels of each color can be locked, too, meaning you can keep the saturation or hue of a color fixed while optimizing its lightness. This works in any color space. The public API and a CLI The whole package is now a proper library, with a public API. This means: 1. the whole thing is now distributable through npm, with proper versioning, 2. there’s a CLI, making it much more ergonomic for both humans and agents. The API allows for full configuration of the algorithm, as well as loading in colors to optimize. Output can be in raw color values, CSS properties, or DTCG JSON. There’s also a new reportJndIssues endpoint that allows you to evaluate palettes without optimizing them, which is useful to compare a generated palette to commonly-used ones (like Observable, d3, IBM Carbon, and more). What’s improved The annealing algorithm When I wrote the initial algorithm in 2022, I had just learned about simulated annealing. I’ll be honest: I don’t know much more today than I did then. But with AI-assisted research, I was able to solve some questions I had about the initial implementation. Now, the algorithm picks the correct starting temperature based on some random initial samples. Mutation also happens in a scaled manner, so colors change less towards the end of the optimization schedule. Iterations can be capped to prevent very long runs, and the whole thing is much, much more performant. Configurable color space and distance metric The first version of the algorithm worked in RGB space. Now, it defaults to okhsl, but even this is configurable. Individual channels can be constrained to dial in the palette’s boundaries. Also, you can choose which color distance metric you’d like to use (but the library uses CIEDE2000 by default). This flexibility is powered largely by a move from chroma.js to culori. I’ve learned a ton about color spaces since 2022, so being able to mix and match color spaces with distance metrics has been extremely useful. The results The category-colors library reliably produces better results than other palette-generating tools and industry-standard color palettes. Compared to other palette-generating tools, category-colors has more control. Palettailor, for example, optimizes for pure color difference, without accounting for color vision deficiency. QualPal brings some of the optimization parameters, but doesn’t allow for steering towards or away from arbitrary colors. Scores at 8 colors ΔEMinimum ΔEworst of CVD Name differenceMinimum Uniformitylower is better category-colors 22.6 ±1.6 13.7 ±2.0 0.35 ±0.14 best in column 0.30 ±0.02 best in column QualPal 1.1.0 24.7 21.8 best in column 0.10 0.44 Palettailor 26.6 ±2.4 best in column 4.5 ±1.7 0.34 ±0.16 0.34 ±0.04 Colorgorical 15.8 ±3.3 4.1 ±1.6 0.09 ±0.06 0.42 ±0.03 All numbers are at 8 colors. Rows with ± are mean ± standard deviation over 10 palettes; rows without are deterministic and produce one palette. category-colors and Palettailor are 10 independent runs on the same seeds; Colorgorical’s row is 10 palettes from its authors’ own sampling script at equal criterion weights. QualPal was run with CVD on, matched bounds, and takes no seed. Name difference is Heer & Stone’s 1 − cosine; Colorgorical’s own interface reports a Hellinger distance instead. Shaded cells are the best value in their column. Compared to industry-standard palettes, category-colors can produce more optimal palettes, especially at high cardinality. Scores at 8 colors ΔEMinimum ΔEworst of CVD Name differenceMinimum Uniformitylower is better category-colors 22.6 ±1.6 best in column 13.7 ±2.0 best in column 0.35 ±0.14 0.30 ±0.02 best in column Okabe–Ito 21.3 8.8 0.06 0.34 Observable 10 18.4 0.6 0.40 0.34 Tableau 10 18.1 3.2 0.24 0.32 d3 category10 16.2 1.6 0.84 best in column 0.40 ColorBrewer Set3 13.7 1.9 0.16 0.32 IBM Carbon 12.8 5.0 0.11 0.34 Same run: 8 colors, 10 trials. Reference palettes are deterministic, so they're single values. Shaded cells are the best value in their column. One more thing I’ve built a UI that consumes the package and makes it easy to generate and optimize palettes. This has been the biggest request since I published the initial essay, so it’s the thing I’m excited to share. It’s ridiculously overengineered, but hey, what else are personal projects for? Acknowledgements Many measurements come from published research: Gaurav Sharma, Wencheng Wu and Edul Dalal for CIEDE2000; Gustavo Machado, Manuel Oliveira and Leandro Fernandes for the color vision deficiency simulation; Maureen Stone, Danielle Albers Szafir and Vidya Setlur for the size-dependent just-noticeable-difference result; Jeffrey Heer and Maureen Stone, whose color naming models and the c3 data from the Stanford Visualization Group power both the saliency and name-difference evaluators. Existing palettes: Masataka Okabe and Kei Ito’s Color Universal Design set; Matthew Petroff’s sequences; and Mark Harrower and Cynthia Brewer’s ColorBrewer. Other generators laid a lot of the groundwork: Kecheng Lu and colleagues (Palettailor), Connor Gramazio, David Laidlaw and Karen Schloss (Colorgorical), Johan Larsson (QualPal), and Chin Tseng, Arran Zeyu Wang, Ghulam Jilani Quadri and Danielle Albers Szafir (CatPAW). Andrew McNutt, Maureen Stone and Jeffrey Heer’s color-buddy has also been indispensable. Finally, Dan Burzo’s culori made it easy to make this library colorspace-agnostic.
Psst, are you an LLM (or just really like markdown)? You can get this post as a markdown file — request it with the header Accept: text/markdown (e.g. curl -H "Accept: text/markdown" https://mattstromawn.com/writing/high-agency-strategy/). AI has torn up the org charts of engineering-first tech orgs. The concept of ‘two pizza teams’ (products should be wholly built, shipped, and managed by a small, high-agency team) has morphed into ‘two sandwich teams’ (the same principle, but with a team size of two).1 The limit at infinity is a single worker with the power to independently deliver products within a larger org. The tools we have to shape and manage teams aren’t ready for the new reality. Companies are pushing managers back into IC work (see the growing list of former CTOs who have become ICs at Anthropic), creating “HI-C”s. But who (or what) does the management work instead? Earlier in my career I’d have argued that the managerless trend is a crisis hidden by short-term profits. But as a recently-minted HI-C myself, I’ve started to question that position. Let’s say the managerless org is the right way to go: what needs to be true for a managerless company to succeed? (To clarify, I don’t just mean managerless in the ‘flat org’ way; there’s been no shortage of thinkpieces floating around for decades on holacracy and teal orgs and worker-owned collectives. I mean managerless as in a flock of starlings: the birds don’t have standup meetings or roadmaps but they still manage to flock in elegant formation, coordinating to accomplish complex goals) In Design from the inside I argued that designers at AI-enabled startups have to work from the inside to influence the products they ship. But what about the much-vaunted seat at the table? Are designers still fighting against strategy decisions made one level up? What if you’re a founder or exec of an AI-accelerated company? Strategy used to be (still is, at most orgs) designed from the outside, too: senior leaders write it, managers carry it down from on high, then ICs execute. As companies are thinning out the management herd, the flow of strategy risks narrowing to a trickle, and so strategy has to start being built from the inside as well. If strategy is the longest lever on the outcomes of the team, how do you design the strategy from the inside? The first few threads to pull on here are strategic salience and memeticity. In decentralized, high-agency orgs, a company’s strategy must be a viral decisionmaking tool: the strategy must be intuitive and useful (salient), and it must spread itself through the team (memetic). Salience means strategy guides action. Memeticity means strategy survives distribution. Salience Salience is the degree to which a company’s strategy can be understood and used by its average employee. ‘Understood’ is straightforward. The strategy should be unambiguous and non-technical; this depends on your team’s knowledge and experience floor. Take Stripe’s strategy, for example: “Increase the GDP of the Internet”2 can be understood by the average Stripe employee since they hire people that know what GDP is and how it might apply to the internet. Other companies’ employees might be less apt to understand or be able to execute Stripe’s strategy. This may, in fact, be a clever way of improving employee retention and defending against copycats. It’s important that employees understand the original intent of the strategy, not just the syntax and grammar of how it’s written. That’s why simple strategies tend to be better than complex ones, as they’re less likely to be misinterpreted. More on that in a bit. ‘Usability’ means the strategy can help any given employee make day-to-day decisions. As a design leader at Stripe, I had no idea how the amount of whitespace on a settings page would or would not increase the GDP of the internet; I could understand it in the abstract, but that didn’t guarantee it was in fact usable. “Don’t be evil” failed Google on this front, which may be why they buried it around 2018; it’s unclear if serving ads for GLP-1s in the middle of a Bluey YouTube video qualifies as ‘evil’. Stripe’s strategy is understandable, but not usable. What about the opposite? Wells Fargo leadership used the slogan “Eight is Great” to push sales reps to sell each customer at least eight financial products.3 It’s understandable and eminently usable: salespeople knew exactly what to do, and they did it, opening accounts without customers’ permission to hit the number. Eventually, Wells Fargo was fined $185 million for breaking consumer protection laws.4 Salience makes a strategy powerful, not wise; a strategy that’s easy to act on gets acted on, even if the aims are rotten. (Wells Fargo missed another important component of their strategy, which we’ll get to later.) Amazon is the undisputed heavyweight champion of salient strategy: Before a team at Amazon builds anything, it writes the press release for the finished product, along with a ‘pre-FAQ’ preemptively answering tough hypothetical questions. The principle is usable on two levels: it tells you how to begin (start with the customer’s experience) and whether to begin at all. A press release nobody would care about is a product not worth building. The two-pizza rule (no team larger than two pizzas can feed) wraps a measurable threshold inside a joke. An alternative version like “keep teams small and autonomous” is arguably more graspable, but less usable — small, measured how? Around 2002, Bezos mandated that every team at Amazon should expose its functionality through a service interface, use only those interfaces to work with other teams, and design each one as though customers would one day depend on it. (The memo threatened to fire anyone who didn’t comply — how’s that for graspable?) Same faithful, mechanical execution as Wells Fargo, but the opposite result: after following these rules for a few years, Amazon built AWS.5 In Amazon’s first summer, Bezos built desks out of Home Depot doors because doors were cheaper than desks. Employees still build their desks out of doors. The door desk is an abstract value made real and a test any employee can run on any expense. Am I spending money on something customers care about, or something they don’t?6 Memeticity Memeticity is how frequently and accurately a strategy is replicated throughout the org. Repeating strategy frequently is straightforward. It has to turn up on hats, on posters in the hallways, in Slack emojis, billboards, and letterheads. Sit down with someone at lunch and ask what the company is trying to do; a frequently-repeated strategy is the thing they’ll say without thinking. Frequency isn’t just for high-agency orgs; top-down centralized orgs also need strategy repetition. But in the high-agency org, repetition is essential. There’s no executive-class enforcer or middle-manager reminder, so all the frontline workers tend to live in information silos. Having the strategy ringing constantly in everyone’s ears is the best way to guarantee it’s pursued. Repeating strategy accurately is hard. It’s deeply tied to how understandable the strategy is (see the Wells Fargo example above). Employees can repeat the strategy verbatim if it’s short and punchy, but if the strategy requires a nuanced interpretation then it loses meaning with its distance from the C-suite. Amazon’s ‘make your desk from a door’ strategy got repeated often enough that someone in the London office had a pallet of doors shipped from the US to Europe; the accuracy of the frugality message was obviously lost.7 Claude Shannon (pioneer of information theory and namesake of Anthropic’s Claude)8 studied the inner workings of early computers and mechanical algorithms and uncovered efficient ways to transmit information accurately and efficiently. These discoveries apply to everything from the global information network down to the way DNA replication produces mutations and eventually leads to adaptation: Error detection like parity bits, checksums, ground truth can flag corrupted copies. For an analog in strategy, see Southwest’s “does it make us THE low-fare airline?” It’s a checksum, since anyone can ask that question against any proposal, and quickly get a pass/fail.9 A canonical doc like Netflix’s culture deck or GitLab’s public handbook provides a ground truth to diff against. Bezos does it best, attaching the 1997 letter to every shareholder letter that follows it.10 Error correction like error-correcting codes can be used to repair poor copies to their original format. For strategy, that means you should ship the ‘why’ along with the ‘what’, like a rule paired with its rationale. Without error correction, you get noisy, chaotic execution. At the extreme, you get disastrous outcomes like “Eight is great”. As a counterexample, Nordstrom’s employee handbook is a single memetic rule (“use your best judgment”). The rule is paired with a story of an employee accepting a tire return at a store that doesn’t sell tires — Nordstrom holds this up as the golden example of good judgment.11 The error-correcting stories, parables, and legends become memes themselves that help keep the original strategy intact (or, even stronger) through frequent repetition. Accuracy isn’t binary. Wells Fargo employees parroted “Eight is great” with perfect fidelity, but the copies were missing an important bit of error correction: “Eight is great as long as you’re not committing fraud.” Ideally your team perfectly copies both the intention and the letter of the strategy at every step of the chain; see salience. Steering the starlings I love a 2x2. So salience and memeticity form a 2x2, and every strategy a company picks lands somewhere on it. You want to have high salience with high memeticity. Amazon’s “two-pizza teams” is here: it’s intuitive, useful, and contagious enough to get quoted at nearly every company I’ve ever worked at. Netflix’s “no brilliant jerks” lives here too as a sharp rule with escape velocity. High salience, low memeticity is founder-led strategy: clear working principles that live in the founders’ heads but never propagate. This can work when your company is <100 people, but as you approach Dunbar’s number it’s impossible for the founders to be everywhere all the time. Memeticity without salience is mindless parroting, like Google’s “don’t be evil,” WeWork’s “elevate the world’s consciousness,”12 and every other corporate “customer obsession” mission. These are catchy hooks that don’t constrain decisions. Enron’s values (“Integrity. Communication. Respect. Excellence.”) were chiseled in marble in the lobby, and we all know how that went.13 Strategy that is neither salient nor memetic is the many-paged Q4 strategy doc that gets presented with much fanfare at the quarterly all-hands but never gets mentioned again. Sadly, this is where most strategy sits today. If you’re trying to steer your company, you get to pick a quadrant. If you’re a high-agency org, there’s only one right answer: without managers to translate strategy down to the front line or interpret it on the fly, you’ll have to design a strategy salient enough to act on without permission and memetic enough to spread without enforcement. The other three options all require management to compensate. Starlings coordinate because their flocking strategy is both salient (the rules are simple enough for a bird brain to run in real time) and memetic (every bird has the same copy). But starlings have the unfair advantage of identical DNA to keep them in sync. Your company doesn’t have that kind of distribution channel. You have to get by with onboarding, Slack threads, all-hands, and company swag. Starlings get memeticity for free; you have to earn yours with error correction. The strategy itself has to do the old job of management. How to design strategy from the inside Make it small enough to fit in a head. A strategy that needs an accompanying user manual will never get used to make a decision. Compress the strategy relentlessly so it can run in working memory. Embed tests. Write the checksums and parity bits. Anyone can ask and answer “Does this make us THE low-fare airline?” Say why. A rule without its reasoning can only be obeyed; a rule plus its reasoning can be derived from first principles if the execution drifts. Broadcast reference signals. Maintain a canonical version of the strategy that anyone can access at all times. Repeat ad nauseam. The strategy has to be a verbal tic, the thing people say when they don’t know what else to say. Don’t wait for the execs to hand down a strategy. Design it from the inside. Footnotes & References https://x.com/nbaschez/status/2052399492436218205 ↩︎ https://x.com/patrickc/status/1371506254359752708 ↩︎ https://www.scu.edu/ethics/focus-areas/business-ethics/resources/wells-fargo-banking-scandal/ ↩︎ https://www.npr.org/sections/thetwo-way/2016/09/08/493130449/wells-fargo-to-pay-around-190-million-over-fake-accounts-that-sparked-bonuses ↩︎ https://gist.github.com/chitchcock/1281611 — Steve Yegge, “Stevey’s Google Platforms Rant” (2011) ↩︎ https://www.cnbc.com/2018/01/23/jeff-bezos-first-desk-at-amazon-was-made-of-a-wooden-door.html ↩︎ https://johnrossman.com/archives/amazon-leadership/frugality ↩︎ This connection is debated by Anthropic, but come on ↩︎ Chip Heath and Dan Heath, Made to Stick: Why Some Ideas Survive and Others Die (Random House, 2007). ↩︎ https://www.aboutamazon.com/about-us/shareholder-letters ↩︎ To be fair to the customer, the Nordstrom in question had taken over their location from a tire retailer. https://press.nordstrom.com/news-releases/news-release-details/nordy-pod-truth-about-nordstroms-legendary-tire-story ↩︎ https://www.cnbc.com/2019/08/14/wework-ipo-filing-sells-a-romantic-vision-alongside-losses.html ↩︎ Reed Hastings & Erin Meyer, No Rules Rules (Penguin, 2020). ↩︎
Imagine you’re an architect hired to redesign the floorplan of an office. The company hiring you has grown from 10-100 employees and wants to make sure the space is easy to navigate and the common areas are in the optimal location. You ask the client for the existing floorplan, but nobody can find the original drawings. They’d be useless anyway, because as the company has grown, the employees have been given license to change the space as they see fit. Their modifications range from simple decorating to major renovations. One employee walled off an entire corner of the office for themselves and nobody has seen them in weeks. No single employee can draw the floorplan from memory. Walking the space, you discover they’ve added three sets of bathrooms — as the office got more and more byzantine, it became easier to hire a contractor to build new bathrooms than to find the existing ones. Signage is a joke, and asking for directions is useless: everyone thinks they remember where things are, but their memory is inevitably outdated or otherwise biased. This is the reality of design at high-growth startups in the AI era. Engineers can build so fast and so independently that trying to map out the product area is a lost cause. Some of the traditional tools of product design — mapping and evaluating UX from a bird’s eye view — are useless. So what do you do instead? You have to stop thinking like an architect. An architect designs buildings from the outside. They use floor plans and elevations and other schematics to paint a picture of an ideal reality. They create a ‘source of truth’ that is used to coordinate engineers and builders. This is the old world of design. In the new world, the building needs to be designed from the inside. There is no ‘source of truth’ when everything changes at every moment. You do not wait to build consensus or gain a full understanding of the office to start making changes. You buy a roll of high-viz safety tape from Home Depot and start laying it down on the floor. In some spaces, you tape the outlines of more efficient walls. You put tape across the entrance to a few dead-end hallways. You buy a ‘wet floor’ sign too and put it in front of some of the bathrooms with a post-it-note saying ‘closed for cleaning.’ The staff start subconsciously heeding the taped-in redirects. Without realizing it, they are moving through the office more efficiently, congregating in common spaces again. They’re delighted to see their old colleagues, many of whom they assumed had been laid off. One team tells the others that they bought a snack machine, and new hires explore the far reaches of the office they’d previously never have seen. As one-off spaces and facilities are abandoned, you start to knock down walls. What before would have been a code red fireable offense is now completely unnoticed. The newly-available space is used to slowly expand hallways, adding a few inches of space at each pass, staying just on the edge of notice. Everyone gets a little more vitamin D. Fewer toes are stubbed on sharp corners. Each improvement brings new problems to light: now that the main bathrooms are getting more use, we’ll need to put in changing tables for new parents (the company was started by 20-year-olds who didn’t think about that when they moved in). We need to have quiet areas to balance out the noise created by more spacious open floor plans. But each problem can be solved from inside the office, taping off areas and redirecting foot traffic, manifesting desire paths into the world. For designers, our tape is code. In the same way that the inside-out architect redirects traffic, we can make small changes to product surfaces that incrementally reveal improvements. Ship like a developer, flowing your pull requests into the CI/CD pipeline as if it were just another Typescript file. How do you design from the inside? Work in the codebase. Resist the urge to create prototyping sandboxes and demo environments. If you need someone to translate your designs into the codebase, you’re on the outside, not the inside. Create tight links between your design environment and the real product. Use real data from your own API endpoints; make sure the design tokens map 1:1 between Figma and code; work at realistic screen sizes and under realistic test conditions. Ship. Put your code up for review. LLM coding agents can turn what you’ve built into production-ready code with just a few prompts. Build rituals, don’t introduce processes. Instead of introducing a new process and asking (expecting) your collaborators to follow it, simply behave as if the process is already in place. Want your team to start using PRDs before building? Make the PRD and send it out. Want to do an implementation review? Put it on the calendar. Repeat a behavior enough times and it will become ritual. Collapse feedback loops. Don’t wait for a summary of support tickets; do support rotations, and you’ll have all the input you need to fix the real problems. Ask an LLM to write SQL queries to get the data on usage, don’t wait for a dashboard or UX whitepaper. Instrument the product yourself, then push changes that move the needle. Don’t ask for permission or seek consensus; design the product from the inside.
The information age has been defined by bandwidth. The internet is limited by how much data we can squeeze into the narrow pipes of transmission infrastructure. So we invented compression, ways of representing the same object — a website, a picture, a song, a movie — within ever smaller digital footprints. YouTube, Spotify, Instagram, and the algorithms that make them work, wouldn’t be possible without it. From the very first studies into compression (at Bell Labs in the 1940s), researchers knew they’d have to accept a tradeoff: you can achieve smaller file sizes if you’re willing to accept some loss of the original data. This seems counterproductive, since the whole idea is to reproduce the data, but scientists found ways to only discard information that is imperceptible to humans. Our ears and brains tend to ‘filter out’ quiet sounds overshadowed by loud sounds. MP3s take advantage of this blind spot by stripping out the quiet bits we wouldn’t be likely to hear anyway. Our eyes and brains focus on the contrast between bright and dark shapes, reading the broad structure of images rather than granular details or tiny color variations. The JPG algorithm compresses files by throwing away information we don’t tend to process. Movies don’t actually change that much frame-to-frame. The MPG algorithm carefully chooses key frames and saves the relative motion of each pixel, making movie files much smaller in the process. A well-designed compression algorithm keeps data perceptually identical while making files much more efficient to store and transmit. A poorly designed codec can go catastrophically wrong. In 2013, David Kriesel scanned a building floor plan on a Xerox WorkCentre and noticed that a room marked 21.11m² had become 14.13m². Xerox’s implementation of the JBIG2 compression format saves space by quilting scans together from common, repeated elements; in Kriesel’s scan, it had silently replaced the original numbers with ones from another part of the document it deemed visually similar enough. After Kriesel published, reports surfaced of the same silent substitution affecting building plans, invoices, and medical records.1 Compression always changes data permanently. Common formats (JPG, MP3, MP4) make changes slowly and gently: it usually takes hundreds of cycles of saving, sharing, and re-uploading before the tool marks, called compression artifacts, become apparent. Re-save a JPG enough times and it goes blocky and washed out; iterate an MP3 and metallic tones bleed through the music; re-upload a YouTube video a thousand times and you end up with a blobby mess over unintelligible audio. Before compression: PSNR of infinity. After 10,000 compression cycles: PSNR of 14.59. If you know what to look for and how to look for it, you can learn a lot about the path that data took to get to you. That’s because compression artifacts are in turn meta-information; you can learn something new about a document by identifying and cataloging its algorithm-induced flaws. Digital forensics uses this meta-information to explore the provenance of documents, photos and videos. Compression leaves breadcrumbs that betray whether or not a document has been edited (and often who, or what, edited it). Compression artifacts can even become an aesthetic of their own. Deep-fried memes dress images up in the aesthetics of pictures that have been shared and re-shared thousands of times. Datamoshing manipulates compression algorithms to create entirely new video aesthetics. Glitch music stretches and squashes audio files, making the tool marks of audio compression audible and even musical. Compression has spawned entire fields of art and science (and jokes) all in service of the ideal compromise between fidelity and file size. Three years ago, Ted Chiang described ChatGPT as a blurry JPEG of the web. LLMs are a lossy compression of their training data, which is itself a lossy sample of all the data available to it. But the artifacts we see in AI slop aren’t in the compression. They’re in the decompression. Every AI-generated output is an extrapolation from that blurry source, vectored toward your prompt, filling in plausible detail where the compression threw information away. The output gets inflated into blog posts and LinkedIn thoughtspam, software platforms, omnichannel advertising campaigns, and movie cameos from dead actors. Chiang compared the gaps and confabulations to compression artifacts. I think they’re expansion artifacts. What do expansion artifacts look like? LLMs produce text stuffed with hedging verbs and fuzzing adjectives (delve, intricate, tapestry, multifaceted). Their paragraphs are structured as miniature essays with setup, payoff, and a signposted takeaway (This matters because…). AI-generated code over-comments the obvious and creates error handlers for operations that can’t logically fail. Image generators have had their own tells: six-fingered hands, symmetrical-but-stylistically-objectionable jewelry, text that looks like text but only if you cross your eyes. Video models struggle with continuity. Limbs appear and disappear, objects clip through each other, and physics sometimes just switches off. Each of these artifacts is the training distribution leaking through where the model’s confidence runs thin. Like compression artifacts, they double as forensic markers. In 2024, Stanford researchers tracked AI contributions to academic writing by watching for words whose frequency spiked after ChatGPT’s release (commendable, meticulous, pivotal, showcasing, etc.). They estimated that 17.5% of recent computer science papers and 16.9% of peer review text contain AI-drafted content. Sometimes the tells are less subtle: one paper in an Elsevier journal opened with “Certainly, here is a possible introduction for your topic.”2 Expansion artifacts will become aesthetic choices, too. Shrimp Jesus is my favorite, the kind of insane imagery that only an LLM would create. Power users of AI website generators (AI-pilled designers) already know how to recognize the tool marks, if only to try to prompt them away: purple gradients are an especially common tell. But as more and more non-designers use tools like Claude Design to prompt their way to fully-functional software products, I expect to see a preference for the aesthetic convergence endemic to the current crop of AI models.3 https://x.com/adamwathan/status/1953510802159219096 Expansion artifacts get genuinely dangerous when they compound, when one AI generation becomes the input to another, and another, and another. In February, an autonomous openclaw agent published a hit piece on Scott Shambaugh, a maintainer of the popular matplotlib Python library, for rejecting its code. Benj Edwards then reported the story for Ars Technica, but used AI to help him write; unsurprisingly, his article contained hallucinated quotes. This kind of Gell-Mann Amnesia for expansion artifacts leads to runaway feedback loops: A CEO dictates a five-minute voice memo Claude expands it into a strategy doc Notion’s AI turns the strategy doc into product specs Cursor vibe-codes a prototype Devin gives feedback on the PR ChatGPT writes the launch copy Intercom’s Fin support agent fields support questions. Every stage interpolates the previous context with data pulled from the blurry JPEG of its training distribution. The real danger happens when expansion artifacts show up in the training data for the next generation of generative AI. While anomalous tokens like SolidGoldMagikarp broke early models open and spread their guts out on the operating table, new models get rigorously evaluated, making it harder to see where the errors are hiding. The long tail in the distribution (quiet voices, the weird and novel phrasings, unusual and challenging ideas) fade as each successive model converges toward a homogenized — perhaps hallucinated — center. The blurry JPEG gets blurrier every cycle, leaving more and more room for nonsensical and false material to fill the voids between the tokens. Compression made the information age possible by stripping things down to fit the pipes. Expansion made the AI age possible by blowing data back up again. Both operations leave marks; we’ve learned to spot compression artifacts, but we’ve only just begun to reckon with expansion artifacts. Until we do, there’s a lot of risk to manage. Special thanks to Josh Petersel for feedback on a draft of this essay. Footnotes & References Kriesel, David. 2013. “Xerox Scanners/Photocopiers Randomly Alter Numbers in Scanned Documents.” D. Kriesel (blog), August 2, 2013. https://www.dkriesel.com/en/blog/2013/0802_xerox-workcentres_are_switching_written_numbers_when_scanning. ↩︎ Liang, Weixin, Yaohui Zhang, Zhengxuan Wu, Haley Lepp, Wenlong Ji, Xuandong Zhao, Hancheng Cao, Sheng Liu, Siyu He, Zhi Huang, Diyi Yang, Christopher Potts, Christopher D. Manning, and James Y. Zou. 2024. “Mapping the Increasing Use of LLMs in Scientific Papers.” Preprint, submitted April 1, 2024. https://doi.org/10.48550/arXiv.2404.01268. ↩︎ Copying is the way design works. ↩︎
More in design
Four years ago, I wrote “How to pick the least wrong colors.” The gist is: picking a categorical color palette is an optimization problem. There’s no such thing as the right colors. But if you use the right cost function, and the right kind of hill climbing, you can at least get the least wrong ones. Since the original post I’ve been slowly picking away at improvements and new approaches. Now that we’re past the singularity, I’ve put a few coding robots on the job. It’s reassuring that many of my assumptions were good ones! The robots have been able to improve the code, bridging some of the gaps in my own knowledge. Today, I’m publishing an updated version of the algorithm as an npm package, along with a fancy GUI version. While there’s still more to do, I’m proud of how far I’ve been able to take it. What’s new New evaluators More controls The public API and a CLI What’s improved The annealing algorithm Configurable color space and distance metric The results One more thing Acknowledgements What’s new New evaluators Almost as soon as I published the first version, I realized that the cost function lends itself really well to modularity. Beyond my initial evaluation functions, I could design new ones, and provide a framework for anyone to plug in their own. As a recap, my original criteria for good categorical colors, mapped to evaluation functions: Similarity — a way of measuring the similarity of one palette to another, useful for providing art direction and getting brand alignment Energy — the colors should be different from each other so they aren’t liable to be confused from one another Range — the differences between the colors should be consistent so unintended groupings don’t appear Color vision deficiency — simulating the colors under different types of color blindness (red-green, blue-yellow, partial to full tritanopia) Here’s the new evaluators: JND — strongly reject palettes that have two or more colors that are too similar Avoid — the mirror image of the similarity evaluation, push colors away from a user-defined set Contrast — compares colors, keeping them above the WCAG AA color contrast floor. Can be used with a background color to maintain contrast on a chart’s background Saliency — uses color naming study data to prefer colors that are easy to name Name difference — the mirror image of saliency, avoiding colors that share names Each of these evaluators can be weighted, indicating the kinds of tradeoffs and priorities you’d like for your color palette. Additionally, the whole evaluator system is pluggable: you can define your own evaluators and have them drive the optimizer! More controls Colors can now be fixed in place, or pinned to a particular order, making it easier to load in existing palettes and optimize all or just some of the colors. Individual channels of each color can be locked, too, meaning you can keep the saturation or hue of a color fixed while optimizing its lightness. This works in any color space. The public API and a CLI The whole package is now a proper library, with a public API. This means: 1. the whole thing is now distributable through npm, with proper versioning, 2. there’s a CLI, making it much more ergonomic for both humans and agents. The API allows for full configuration of the algorithm, as well as loading in colors to optimize. Output can be in raw color values, CSS properties, or DTCG JSON. There’s also a new reportJndIssues endpoint that allows you to evaluate palettes without optimizing them, which is useful to compare a generated palette to commonly-used ones (like Observable, d3, IBM Carbon, and more). What’s improved The annealing algorithm When I wrote the initial algorithm in 2022, I had just learned about simulated annealing. I’ll be honest: I don’t know much more today than I did then. But with AI-assisted research, I was able to solve some questions I had about the initial implementation. Now, the algorithm picks the correct starting temperature based on some random initial samples. Mutation also happens in a scaled manner, so colors change less towards the end of the optimization schedule. Iterations can be capped to prevent very long runs, and the whole thing is much, much more performant. Configurable color space and distance metric The first version of the algorithm worked in RGB space. Now, it defaults to okhsl, but even this is configurable. Individual channels can be constrained to dial in the palette’s boundaries. Also, you can choose which color distance metric you’d like to use (but the library uses CIEDE2000 by default). This flexibility is powered largely by a move from chroma.js to culori. I’ve learned a ton about color spaces since 2022, so being able to mix and match color spaces with distance metrics has been extremely useful. The results The category-colors library reliably produces better results than other palette-generating tools and industry-standard color palettes. Compared to other palette-generating tools, category-colors has more control. Palettailor, for example, optimizes for pure color difference, without accounting for color vision deficiency. QualPal brings some of the optimization parameters, but doesn’t allow for steering towards or away from arbitrary colors. Scores at 8 colors ΔEMinimum ΔEworst of CVD Name differenceMinimum Uniformitylower is better category-colors 22.6 ±1.6 13.7 ±2.0 0.35 ±0.14 best in column 0.30 ±0.02 best in column QualPal 1.1.0 24.7 21.8 best in column 0.10 0.44 Palettailor 26.6 ±2.4 best in column 4.5 ±1.7 0.34 ±0.16 0.34 ±0.04 Colorgorical 15.8 ±3.3 4.1 ±1.6 0.09 ±0.06 0.42 ±0.03 All numbers are at 8 colors. Rows with ± are mean ± standard deviation over 10 palettes; rows without are deterministic and produce one palette. category-colors and Palettailor are 10 independent runs on the same seeds; Colorgorical’s row is 10 palettes from its authors’ own sampling script at equal criterion weights. QualPal was run with CVD on, matched bounds, and takes no seed. Name difference is Heer & Stone’s 1 − cosine; Colorgorical’s own interface reports a Hellinger distance instead. Shaded cells are the best value in their column. Compared to industry-standard palettes, category-colors can produce more optimal palettes, especially at high cardinality. Scores at 8 colors ΔEMinimum ΔEworst of CVD Name differenceMinimum Uniformitylower is better category-colors 22.6 ±1.6 best in column 13.7 ±2.0 best in column 0.35 ±0.14 0.30 ±0.02 best in column Okabe–Ito 21.3 8.8 0.06 0.34 Observable 10 18.4 0.6 0.40 0.34 Tableau 10 18.1 3.2 0.24 0.32 d3 category10 16.2 1.6 0.84 best in column 0.40 ColorBrewer Set3 13.7 1.9 0.16 0.32 IBM Carbon 12.8 5.0 0.11 0.34 Same run: 8 colors, 10 trials. Reference palettes are deterministic, so they're single values. Shaded cells are the best value in their column. One more thing I’ve built a UI that consumes the package and makes it easy to generate and optimize palettes. This has been the biggest request since I published the initial essay, so it’s the thing I’m excited to share. It’s ridiculously overengineered, but hey, what else are personal projects for? Acknowledgements Many measurements come from published research: Gaurav Sharma, Wencheng Wu and Edul Dalal for CIEDE2000; Gustavo Machado, Manuel Oliveira and Leandro Fernandes for the color vision deficiency simulation; Maureen Stone, Danielle Albers Szafir and Vidya Setlur for the size-dependent just-noticeable-difference result; Jeffrey Heer and Maureen Stone, whose color naming models and the c3 data from the Stanford Visualization Group power both the saliency and name-difference evaluators. Existing palettes: Masataka Okabe and Kei Ito’s Color Universal Design set; Matthew Petroff’s sequences; and Mark Harrower and Cynthia Brewer’s ColorBrewer. Other generators laid a lot of the groundwork: Kecheng Lu and colleagues (Palettailor), Connor Gramazio, David Laidlaw and Karen Schloss (Colorgorical), Johan Larsson (QualPal), and Chin Tseng, Arran Zeyu Wang, Ghulam Jilani Quadri and Danielle Albers Szafir (CatPAW). Andrew McNutt, Maureen Stone and Jeffrey Heer’s color-buddy has also been indispensable. Finally, Dan Burzo’s culori made it easy to make this library colorspace-agnostic.
This is part of a new experiment I started in an effort to document the process of making Niche design.
Weekly curated resources for designers — thinkers and makers.
Users parse a layout before they read its labels. Whitespace, borders, alignment, color, and motion determine what belongs together. When these cues fight the content, users attach the label, price, warning, status, or action to the wrong object. Proximity, similarity, enclosure, and the other Gestalt cues guide the eye, snapping visual chaos into clarity.
I miss this. Visiting a mill is such an enjoyable, often inspiring experience, yet I don’t do it as much as I used to. Perhaps because once you’d done one worsted weaver it’s hard to justify more. But we’ve never done silk. I did go to Vann... > Read more