Five distinctly different collectible figurines on cream plinths showing a progression from tiny and hunched to large and confident

We turned Instagram profiles into characters

The profile photo is already on the row. We copy it off Instagram’s CDN so the board does not rot when they rotate a URL, and we serve it from our own storage. The second picture is new. It is a 3D figurine of the same subject, sitting in a cafe armchair on a flat cream backdrop, generated once per listed handle from that photo plus the public statistics we stored when the handle was added.

The model that draws the figurine is not choosing a personality. It receives a brief: pose, expression, wardrobe, props. The brief is the output of a derivation that only reads columns we already have — follower count, following count, post count, verified, private, business, and whether there is a link in bio. No extra scrape. No guessed vibe.

That is the whole system. One handle, one character, one paid render. The part worth writing down is the rules that produce the brief. Those rules are the joke, and they are the only part we can change without spending again.

Why a checkable gag beats a pretty toy

Generic character generators are now cheap. You can hand a model a cropped face and get a vinyl figure with studio lighting. The object looks collectible. It is also interchangeable. An account with 800 followers and an account with 80 million come back in the same tasteful chair because the model has a prior about what a “character” looks like. The prior is style. Style is not a fact about the row.

A stats-derived character is funnier because you can audit it against the listing. The board already prints follower count, following, verified. If the figurine is wearing indoor sunglasses, you can look at the following number and see whether that was earned. If it is buried in ring lights, you can look at the post count. The laugh is the click of correspondence, not the quality of the plastic.

Pretty fails that test. You look at the row, then at the toy, and there is no mapping. Two different lives produced one costume. Stats-derived passes when the mapping is rude and exact. The Ghost is not shy. The Ghost is under a thousand followers. The Main Character is not confident. The Main Character has more than a million people on the public counter. Swap the numbers and the silhouettes should become wrong. If they do not, we built a renderer, not a translation.

The model will make both of them look like toys. That is what we pay it for. The derivation is what stops them being the same toy.

One handle, one picture

Every listed handle gets exactly one generated character. The schema treats that as a lock, not a preference. Generation may start only by winning an atomic status change out of none or failed. Two people adding the same username race on that update; one of them spends. A second, independent guard is an idempotency key keyed on the handle, so a retried API call is not a second charge.

The archetype label — “The Ghost”, “The Grinder” — is stored on the row. The card can print it without re-running the function. A later edit to the rules will not silently relabel a picture we are not going to redraw. The PNG lives in object storage. The origin URL from the image API is kept for provenance. Serving does not depend on it.

This constraint is why the derivation has to be right before the call goes out. Regenerating the board is how you empty the credit ceiling. Editing a threshold in the tier table is a diff. We will do the second. We will not do the first for fun.

The ladder is follower count

Five base characters. One column. Tiers are ordered from high to low so the first match wins.

Under 1,000 followers is The Ghost: a tiny figure in an enormous hoodie, sleeves swallowing the hands, sitting bolt upright in an armchair that is too large, feet not reaching the floor, a single cup of tap water. Wide-eyed, slightly startled. The furniture is sized for someone else.

1,000 to 10,000 is The Grinder: hunched over a laptop balanced on the knees, the screen a flat-lining graph, hood up, rumpled, tired, determined, a slight twitch. Empty cups stacked beside the chair. Enough of an audience to justify staring at a chart. Not enough to justify a staff.

10,000 to 100,000 is The Local Legend: sitting forward on the edge of the seat, elbows on knees, a slightly-too-new streetwear hoodie with the tags still on, an iced coffee the size of the torso, an open-mouthed grin. A city knows the handle. The global feed does not.

100,000 to 1,000,000 is The Verified Menace: perched on the arm of the chair mid-gesture, a designer bomber, a phone at arm’s length on a selfie stick, pleased with the sentence. The name is the posture of the band. It is not a claim that the account is verified. The badge is a separate modifier. An unverified account at 120,000 still sits here. A verified account at 400 still sits with the Ghost, plus a chain.

Over a million is The Main Character: lounging back, one leg crossed, taking up the whole seat, an expensive plain black tee, a comically tiny espresso between two fingers, serenely bored. Scale inverts the Ghost. Same chair. Opposite occupancy.

The gradient is body language, not decoration. Decoration is the next layer. Placed on one board, the five silhouettes should tell you the rank without the shortened follower string. We still print the string. The figurine is so the string has a body.

a tiny vinyl figurine in an enormous hoodie sitting bolt upright in an oversized cream armchair, feet dangling above the floor, holding a single cup of tap water

The tier decides the silhouette. You can read the rank from across the room.

Columns that stack

Modifiers append. They do not replace the tier. Each one maps to a column already on the profile row.

A verified account gets a gold chain with a blue checkmark pendant.

An account with 10,000 or more followers that follows fewer than 100 people back gets sunglasses worn indoors. The expression becomes unreadable.

An account following more than twice its follower count gets a fistful of “follow me back” flyers.

1,000 or more posts means the figure is buried in ring lights, tripods, and cameras.

Between 1 and 10 posts means one crumpled printed photograph, clutched.

A private account wears a paper bag with a smiley drawn on it. The public label becomes The Private Mystery.

A business account wears a tiny ill-fitting suit and carries a clipboard.

A profile with a link in bio gets a small cardboard sign reading LINK IN BIO, propped against the chair.

They accumulate. A verified million-plus account that follows almost nobody and sells something in the bio gets the espresso, the chain, the glasses, and the sign. That is four branches, not a custom prompt.

Jeff Bezos, from our board, is the clean case. The public profile scraped into 4,988,487 followers, verified, following 48. Derivation: The Main Character, plus the chain, plus the indoor sunglasses. The espresso is the tier. The jewellery is is_verified. The glasses are 48 against five million. We did not write a Bezos rule.

Cristiano Ronaldo is the other stack. The public profile scraped into 679,640,459 followers and more than 4,000 posts. Same top tier. The post-count branch fires at a thousand and stays on, so he is buried in camera gear. The function does not know he is a footballer. A working photographer at 1,200 posts gets the same clutter at Grinder scale. Reusing the column is the gag. Special-casing famous people would be the bug.

a lounging vinyl figurine in a black tee, gold chain with a check pendant, indoor sunglasses, a tiny espresso, a clipboard, and ring lights crowding a cream armchair

Every prop maps to a database column. That is why it lands.

Zero posts is missing data

The sparse-post gag looks like it should include zero. Zero is sparser than ten. The shipped rule refuses that.

A post count of exactly 0 is treated as missing scrape data, not as a real signal. The scraper that fills the row sometimes returns 0 for accounts that plainly have posts. If you take the zero literally, the 1-to-10 branch fires and the figure clutches a crumpled photograph. We watched that happen to a 500-million-follower verified account. On a card that is supposed to be a translation of the listing, it reads as we cannot count, because that is what it is.

The guard is a floor. The photograph only attaches when posts_count is at least 1 and at most 10. The studio clutter only attaches at 1,000 or more. Zero adds no post prop. The tier still applies. The chain still applies. The glasses still apply. We just do not invent a biography from a known-bad null.

This is a correctness rule inside a comedy table. It has to live there, because the table is the only place the joke can be wrong in a way a patch can fix. The model will draw the brief it is given. A pretty picture of a lie is a louder lie.

We did not add a famous-people exception. We stopped sending the instruction.

When the avatar is not a face

The style contract tells the model to keep the recognisable face, hairstyle, and skin tone. That sentence assumes a person. Brand and product accounts routinely use a logo, a wordmark, a shoe on white. Those accounts do not have a jawline to preserve.

Leave the contract alone and the model manufactures a face out of contours that were never a face. The result is a smear. It is also a misread. The account was never claiming to be a person in a hoodie.

The brief therefore contains an explicit fallback. If the source image is a logo, a product, or an object rather than a person, invent an original mascot from that logo’s shapes and colours instead of smearing a face that is not there.

We do not run a face detector. The derivation cannot see pixels. It can only put the warning in the instruction. The model either notices or it does not. When it notices, the character is built from the brand’s own geometry. When it does not, we have a melted face and a credit already spent, because we generate once.

The sentence is there because the failure is obvious at thumbnail size, and because retrying is the behaviour the rest of the system is built to prevent.

What it still gets wrong

The table is cheap to retune. The pictures are not.

Change a threshold, add a prop, rename a tier, and every new handle gets the new brief. Every old handle keeps the render and the stored label from the day we spent the credit. That is why the label is stored: so a retune does not relabel a picture we will not redraw. It is also how the board ages. A Grinder who crosses 10,000 followers stays hunched over the flat-lining laptop. The row will show the new count. The figurine will not.

The model drops props. We have asked for a checkmark pendant and received a generic chain. We have asked for indoor sunglasses and received a bare face. We have asked for LINK IN BIO and received unreadable marks, or no sign. Lettering is a weak spot for image models; two of the gags are words on cardboard. Those will keep failing in public.

Modifiers collide in composition even when they are correct in the brief. A paper bag over the head fights “keep the face.” A tiny suit fights the Ghost’s enormous hoodie. Ring lights eat the espresso. The function concatenates wardrobe and props. The renderer negotiates. The object that made the row funny is often the first thing that loses.

The non-face path is still a hope. Brand handles will keep arriving with logos. Some will come back as mascots. Some will not. We will see the smear on the board, and we will not automatically redraw it.

The ratio gags have cliffs. Sunglasses require 10,000 followers, so 9,999 following 20 people is a Grinder with a naked face. The flyer branch in the shipped function also requires following more than 200 people, on top of the 2× ratio, so a small account that follows everyone does not get the flyers. Thresholds make silhouettes readable from a distance. They also make two neighbouring rows discontinuous.

Private should not reach the public board. On add, a private handle is a dead end: we only read what Instagram already shows the public. The paper-bag rule still sits in the function because is_private is a column and the derivation is written against the schema. A rule that never fires on the homepage is not the same as a rule that is correct.

Other scrape lies will be quieter than a zero. A snapshot can be up to six hours old on re-add before we scrape again; the character is older than that by construction. Following counts move. Post counts move. The figurine is a receipt.

The remaining work is more guards in the table, fewer instructions a renderer can shrug off, and a written list of which wrong pictures we will live with because the alternative is paying to draw them again. Handing personality back to the model would make a prettier object. The object would stop being about the row.