← Back to the personasThe method · one real run

24 drafts for one person.

Every frame here is a real draft from one Baste run for Moss, scored by a vision model that looked at it. The circled frames are what Baste kept: the best in each direction it explored.

  1. BasteFour seed drafts, then five rounds that nudge the brief warmer or cooler, sparser or denser.
  2. FitA vision model scores every draft against Moss’s taste, and whether it still works as an interface.
  3. CutThe best in each direction is kept. You choose from the spread, not one guess.

8 kept from 24 drafts

The brief

Drafted from Moss’s measurements

Hero image for a field-notes app about fungi networks: a greenhouse bench at dusk where mycelium glows through moss

Variation seed → kept hero
colourwarm amber and terracotta→ muted, desaturated
densityrich, layered→ balanced
renderingcinematic, lightly stylised→ illustrative, painterly

Mushishi, Ernst Haeckel, bioluminescent, mycelial, solarpunk

Create something that feels like it belongs in Annihilation as interpreted by Ernst Haeckel.

Draft 16 for Moss
Draft 16 · generation 3Kept: warm and dense0.93
Persona alignment
0.95
Visual quality
0.90
Uniqueness
0.95
Coherence
0.90
Usability
0.90
“This image perfectly captures the essence of The Cyberbotanist's world. The bioluminescent mycelial structures, set within a greenhouse at dusk, evoke a sense of hidden complexity and organic growth.”— the judge, looking at the image

Real output from baste generate cyberbotanist --provider fal · Seedream drafts, judged by Gemini through fal. Select any frame to read the judge’s notes.

01 — Measure

A persona is not a demographic.

“Users aged 25–34” has no taste. A person does. They own a stack of films, a playlist, a room they feel at home in, and a list of things they can't stand in software. Baste takes all of that as measurements.

  • Influencesfilms, anime, music, games, artists, fashion
  • Habitatthe spaces, tools and platforms they live in
  • Behaviourhow they discover, what they value, what annoys them
Measurement sheet · No. 001

The Cyberbotanist

Pacific Northwest
solarpunk · biohacking
Films & anime
Annihilation, Nausicaä of the Valley of the Wind, The Fountain, Mushishi
Music
ambient, folktronica, Biosphere, Christina Vantzou
Spaces
greenhouses, herbariums, maker spaces with plants
Tools
Raspberry Pi, Arduino, Notion, Are.na
Obsessions
mycelial network topology, bioluminescent organisms
Can't stand
aggressive notifications, dark patterns, infinite scroll
The cut — aesthetic profile
Colour temperature
Density
Edges
Motion
Typography
Texture
Iconography
Layout
02 — Method

Baste first. Cut later.

Tailors tack a garment together loosely, try it on, and only then sew it for good. Baste does the same with design: it explores many loose options, fits them to the persona, and only then produces the finished assets.

  1. 01

    Measure

    Load or write a persona: influences, habitat, behaviour. Baste infers the aesthetic profile, or you set it by hand.

  2. 02

    Draft

    Each asset type gets its own prompt. An icon is described differently from a hero image or an ambient loop.

  3. 03

    Baste

    Quality-diversity evolution, 5–10 generations. Candidates spread across temperature, density and abstractness; every niche keeps its best.

  4. 04

    Fit

    An LLM judge scores each candidate against the persona, weighted toward whether it feels made for them.

  5. 05

    Cut

    A diverse final set becomes production assets — SVG, images, video loops — packaged with tokens and a suite manifest.

Step 03 · the archive

Many fittings, not one answer.

generation 8/830/35 niches · 5 kept
sparse → densecoolcolour temperaturewarm
Shape encodes the third axis: square tiles are literal, round ones abstract. Stitched tiles are the final archive — the best in each region, chosen for spread.
Step 04 · the judge

Scored for fit before finish.

  • Persona alignmentDoes this feel made for them?30%
  • Visual qualityIs it well executed?20%
  • UniquenessCould a stock generator have made it?20%
  • CoherenceDoes it belong with the rest of the suite?15%
  • UsabilityDoes it survive a real interface?15%
04 — Off the table

Finished pieces, ready to wear.

Generation ends in files you can ship: assets in assets/output/{svg,images,videos}, tokens for whichever stack you use, and a manifest that says why each piece exists.

  • QuiverAIsvg/Icons & illustrationsVector icons, spot illustrations and component art, drawn in the persona's icon style.
  • GPT Image 2images/Images & texturesHero images, backgrounds and surface textures from persona-specific prompts.
  • Veo 3videos/Ambient loopsShort motion loops and transitions timed to the persona's motion profile.
  • BastetokensDesign tokensColour, type, spacing, radii, shadows and easing as CSS, Tailwind, Panda or JSON.
  • OpenPencil.opEditable documentTokens and components as a design file you can keep editing, watched for changes.
  • Baste-suite.jsonSuite manifestEvery asset with its purpose, scores, cultural references and relationships.
:root {
  /* The Night Market Coder - Generated Design System */
  
  /* Colors */
  --color-primary: #e44635;
  --color-secondary: #ebb230;
  --color-accent: #fe12a9;
  --color-background: #150d0c;
  --color-surface: #211816;
  --color-text: #f5ecea;
  --color-text-muted: #b1a09d;
  --color-border: #3a302e;
  --color-warm: #eb6200;
  --color-cool: #18b1cd;

  /* Typography */
  --font-heading: 'Dela Gothic One', sans-serif;
  --font-body: 'Instrument Sans', sans-serif;
  --font-mono: 'Fira Code', monospace;

  /* Spacing Scale */
  --space-0: 0;
  --space-1: 0.5rem;
  --space-2: 1rem;
  --space-3: 1.5rem;
  --space-4: 2rem;
  --space-5: 3rem;
  --space-6: 4rem;
  --space-7: 6rem;
  --space-8: 8rem;

  /* Border Radius */
  --radius-none: 0;
  --radius-sm: 0.125rem;
  --radius-base: 0.25rem;
  --radius-lg: 0.5rem;
  --radius-xl: 0.75rem;
  --radius-full: 9999px;

  /* Motion: Quick and decisive: short moves, crisp stops and readable pauses between them. */
  --duration-fast: 100ms;
  --duration-base: 200ms;
  --duration-slow: 400ms;
  --duration-slower: 800ms;
  --duration-spring: 340ms;
  --duration-ambient: 4000ms;
  --ease-default: cubic-bezier(0.25, 0.1, 0.25, 1);
  --ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-snappy: cubic-bezier(0.25, 0.1, 0, 1);
  --ease-standard: cubic-bezier(0.2, 0, 0, 1);
  --ease-enter: cubic-bezier(0.05, 0.7, 0.1, 1);
  --ease-exit: cubic-bezier(0.3, 0, 0.8, 0.15);
  --ease-emphasis: linear(0, 0.044, 0.145, 0.272, 0.404, 0.527, 0.636, 0.728, 0.803, 0.862, 0.907, 0.94, 0.964, 0.981, 0.992, 0.999, 1.003, 1.005, 1.006, 1.006, 1.006, 1);
  --ease-spring: linear(0, 0.044, 0.145, 0.272, 0.404, 0.527, 0.636, 0.728, 0.803, 0.862, 0.907, 0.94, 0.964, 0.981, 0.992, 0.999, 1.003, 1.005, 1.006, 1.006, 1.006, 1);
  --motion-nudge: 2px;
  --motion-shift: 6px;
  --motion-travel: 12px;
  --motion-press: 0.96;
  --motion-stagger: 25ms;
}

/* The Night Market Coder — motion primitives (generated by Baste)
 * Quick and decisive: short moves, crisp stops and readable pauses between them.
 * Classes: .motion-enter .motion-exit .motion-hover .motion-press .motion-emphasis .motion-ambient .motion-stagger
 */
@keyframes baste-enter {
  0% { opacity: 0; transform: translateY(6px) scale(0.98); }
  100% { opacity: 1; transform: none; }
}

@keyframes baste-exit {
  0% { opacity: 1; transform: none; }
  100% { opacity: 0; transform: translateY(-3px) scale(0.98); }
}

@keyframes baste-emphasis {
  0% { transform: scale(1); }
  30% { transform: scale(1.06); }
  100% { transform: scale(1); }
}

@keyframes baste-ambient {
  0% { opacity: 1; }
  2% { opacity: 0.55; }
  4% { opacity: 1; }
  6% { opacity: 0.75; }
  8% { opacity: 1; }
  100% { opacity: 1; }
}

/* Arrive: content, cards and panels coming into view. */
.motion-enter { animation: baste-enter 200ms cubic-bezier(0.05, 0.7, 0.1, 1) 1 normal both; }

/* Leave: dismissals and content making way. */
.motion-exit { animation: baste-exit 100ms cubic-bezier(0.3, 0, 0.8, 0.15) 1 normal both; }

/* Answer the pointer: lift, swell or slide toward the cursor. */
.motion-hover { transition-property: transform; transition-duration: 100ms; transition-timing-function: cubic-bezier(0.2, 0, 0, 1); }
.motion-hover:hover { transform: translateY(-2px); }

/* Confirm a press: the control gives under the finger. */
.motion-press { transition-property: transform; transition-duration: 100ms; transition-timing-function: cubic-bezier(0.2, 0, 0, 1); }
.motion-press:active { transform: scale(0.96); }

/* Draw the eye once: a new badge, a saved state, a result. */
.motion-emphasis { animation: baste-emphasis 400ms cubic-bezier(0.2, 0, 0, 1) 1 normal both; }

/* Flicker: a sign catching power, then a long readable hold. */
.motion-ambient { animation: baste-ambient 4000ms steps(1, end) infinite normal both; }

/* Sequence: the children of .motion-stagger enter one after another. */
.motion-stagger > * { animation: baste-enter 200ms cubic-bezier(0.05, 0.7, 0.1, 1) 1 normal both; }
.motion-stagger > :nth-child(2) { animation-delay: calc(1 * var(--motion-stagger)); }
.motion-stagger > :nth-child(3) { animation-delay: calc(2 * var(--motion-stagger)); }
.motion-stagger > :nth-child(4) { animation-delay: calc(3 * var(--motion-stagger)); }
.motion-stagger > :nth-child(5) { animation-delay: calc(4 * var(--motion-stagger)); }
.motion-stagger > :nth-child(6) { animation-delay: calc(5 * var(--motion-stagger)); }
.motion-stagger > :nth-child(7) { animation-delay: calc(6 * var(--motion-stagger)); }
.motion-stagger > :nth-child(8) { animation-delay: calc(7 * var(--motion-stagger)); }
.motion-stagger > :nth-child(9) { animation-delay: calc(8 * var(--motion-stagger)); }
.motion-stagger > :nth-child(10) { animation-delay: calc(9 * var(--motion-stagger)); }
.motion-stagger > :nth-child(11) { animation-delay: calc(10 * var(--motion-stagger)); }
.motion-stagger > :nth-child(12) { animation-delay: calc(11 * var(--motion-stagger)); }

@media (prefers-reduced-motion: reduce) {
  @keyframes baste-enter-reduced {
    0% { opacity: 0; }
    100% { opacity: 1; }
  }
  @keyframes baste-exit-reduced {
    0% { opacity: 1; }
    100% { opacity: 0; }
  }
  .motion-enter, .motion-stagger > * { animation-name: baste-enter-reduced; animation-delay: 0s; }
  .motion-exit { animation-name: baste-exit-reduced; }
  .motion-hover, .motion-hover:hover, .motion-press, .motion-press:active { transform: none; transition: none; }
  .motion-emphasis, .motion-ambient { animation: none; }
}

$ baste tokens nightmarketcoder --format css

05 — Thread the needle

One install. Works offline until you need a model.

Every command has a --dry-run that writes the prompts without calling an API. Add keys for OpenAI, QuiverAI or Google when you want finished assets.

Personas

baste list
Every persona, base and custom
baste show cyberbotanist
Full measurements and aesthetic
baste create mine --base cyberbotanist
Extend a base persona
baste decompose https://site.example --deep
Draft a persona from a live site
baste remix cyberbotanist liminalweeb --as moss-vhs
Cross two personas

Make

baste generate nightmarketcoder
Asset suite: icon, hero, background
baste ui-kit liminalweeb
The full UI kit
baste tokens cyberbotanist --format css
Tokens as css · tailwind · json
baste gui
The Studio, wired to your machine
baste mcp
Expose Baste to your agent over MCP

Version

baste init cyberbotanist
Start design history
baste update cyberbotanist colors.primary "#a15d3f"
A semantic, recorded change
baste branch cyberbotanist night-shift
Explore an alternate fitting
baste history cyberbotanist
Every decision, with its reasons