Jasper Lu
Oct 29, 2026

Reinforcement Learning for Design Taste

Models have gotten remarkably good in technical domains such as math and coding. Many software engineers today would admit that LLMs now write better code than they could by hand. However, we haven’t yet seen the same capability jump in creative domains like design.

A large part of this is due to limitations in how models are trained. Reinforcement learning works best when there’s an objective way to check a result. Math problems have an exact answer; coding tasks have test suites. Design, like writing or other subjective domains, has nothing like that. This makes them an awkward fit for RL.

The standard workaround is to use an LLM as a judge: show a model a design, ask it how good it is, and turn that score into a reward. In practice, this doesn’t work that well. Models are good at spotting designs that are obviously bad or break from a design brief, but they tend to be poor arbiters of taste — or, at least, of taste that matches our own.

In this post, we test this by training Qwen3.8-27B on a set of webdev RL environments. To measure the effect of judge taste, we train two versions of the model using different judges and explore how each one shapes the model’s final design taste.

The environments

We train using a subset of the RL environments Xiaomi open-sourced alongside MiMo-V2.6. The release includes 7,780 agentic environments, 2,093 of which are webdev tasks. These are essentially RL environments for design.

You can dig through a few of the tasks below. For a more detailed view, or to see all tasks, check out the explorer here.

MiMo webdev tasks 10 of 2,093 tasks · 64 steps · 30 min agent, 20 min verifier
dasyn_260638_00002 EnglishTailwind35 words

Design a static Tailwind landing page for a moving company, art-deco style like a polished Dribbble shot, with smooth infinite-scroll sections, hero, services, pricing cards, testimonials, packing tips and contact CTA using only mock content.

Harness instructions, shared by every task
CWD: /workspace

Build the website the user asks for, then DELIVER it to /workspace/dist/ — opening
/workspace/dist/index.html in a browser must show the finished site. For any request,
always produce a working website; just build it, don't ask the user to confirm
details.

Workflow:
- Develop however you like under /workspace (write source, run Bash, use a build tool).
- The deliverable is whatever ends up in /workspace/dist/ — that is the ONLY thing
  delivered. For a simple site, writing /workspace/dist/index.html (plus any assets)
  directly is fine; if you use a build step, put its static output into /workspace/dist/.

File operations:
- IMPORTANT: file paths MUST be absolute, e.g. /workspace/dist/index.html.
- All file creation/editing goes through the tools. After a tool call, give only a
  brief summary of what you did — DO NOT paste the file content into your response
  text.
- Write plain HTML/CSS/JavaScript with normal angle brackets (< and >), NOT HTML
  entities. Write the code exactly as it appears in a real .html file; do not
  escape characters.

Requirements — the delivered site in /workspace/dist/ must be complete and functional:
- Proper HTML structure with <!DOCTYPE html> at the entry index.html.
- Tailwind CSS via CDN — <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script> — and/or an inline <style> tag.
- JavaScript, if any, in <script> tags or in .js files under /workspace/dist/.
- IMPORTANT: the entry point MUST be exactly /workspace/dist/index.html, and everything
  it needs (scripts, styles, images, fonts) must sit under /workspace/dist/ alongside it —
  that whole directory is taken as-is and served as plain static files, and nothing
  outside it is delivered. Reference assets by paths relative to the dist/ root.

Plan your approach and prioritize the most critical changes first.

Prefer dedicated tools over Bash when one fits: Read for known paths, Grep for
content search, Glob for filename patterns, Edit/Write for file modification.
Reserve Bash for shell-only operations.

Build a website for the following request:

Commentary on the environments

The environments are diverse in terms of brief language and style. However, while they seem to be diverse category-wise at a glance, it is pretty non-diverse in terms of screen type — most of the briefs ask for either a landing page-style web app. If we were training a real-world design model here, we would want the prompts to be more expansive and cover a larger variety of design categories.

Experiment setup

We are interested in how the choice of judge in training shapes a model’s design taste. To test this, we run two training jobs over the MiMo webdev environments, each with a different judge: one with GPT-6 Sol, and the other with Claude Sonnet 5.5. All other training settings are identical.

Training

All experiments train Qwen3.8-27B with LoRA (rank 32) on Tinker. We choose this model instead of a smaller one because it can already generate working designs from the start. This means we can focus training on shaping its taste rather than its raw technical capability.

Training uses GRPO with CISPO loss. Rollouts run in Modal sandboxes, and we train asynchronously with a max staleness of 1 step. We chose async RL here because early training runs spent much time waiting on straggling rollouts.

Hyperparameters are in the table below.

Full training configuration
model
model Qwen3.8-27B
adapter LoRA, rank 32
reasoning effort medium
optimisation
algorithm GRPO, CISPO loss
importance weight clamp (0.2, 4.0)
learning rate 3e-4
optimizer AdamW, β₁ 0.9, β₂ 0.999, ε 1e-8
weight decay 0.01
gradient clip norm 1.0
groups per step 32
rollouts per group 8
max staleness 1 step
sampling temperature 1.0
harness
max turns 64
max new tokens per turn 65,536
context length limit 131,072 tokens

Many thanks to the Thinking Machines and Modal teams for the credits that made these experiments possible. For reference, excluding judge costs, each 24-step training run cost roughly $7k.

Reward

The prescribed reward for these environments uses two judging passes:

  1. Relative grading. This is a group-pick grading scheme. A judge looks at all N rollouts in a group and picks out the pages that are either clearly better or worse. It judges the group N times, with screenshot order shifted between rounds to remove position bias. Votes are converted to scores using the formula: pick_score = (good_votes - bad_votes) / N.
  2. Fulfillment. A judge grades each page on how well the page follows the brief, from 0 to 1.

After grading, the scores are combined into a reward using the following formula:

r={0if the page crashes or hangs, or fulfillment<0.2pick_score+fulfillment+13otherwiser = \begin{cases} 0 & \text{if the page crashes or hangs, or fulfillment} < 0.2 \\[4pt] \dfrac{\text{pick\_score} + \text{fulfillment} + 1}{3} & \text{otherwise} \end{cases}
The relative grading (group pick) prompt
# Group pick: pick out the clearly better and the clearly worse, do not rank the rest

> One call sees the screenshots of every rollout for the same query (numbered in random order). The task is not to produce a full ranking. Instead:
> pick the pages that are **clearly better than the others** (zero or more) and the pages that are **clearly worse than the others** (zero or more);
> treat the rest as "average". If you cannot see a clear difference across the group, say "no clear difference" explicitly. **Do not force a ranking.**

## Task

Below are {n} full-page screenshots of web pages generated for the same website request, numbered 0 to {n_max} (the order is random and unrelated to quality).

Using **overall aesthetic impression** as the main basis, answer three questions:
1. Which pages are **clearly better than the rest of the group**? (Zero, one or several.)
2. Which pages are **clearly worse than the rest of the group**? (Zero, one or several.)
3. If the whole group is at a similar level and no clear better or worse stands out, judge it "no clear difference".

The website request (only to help you understand what kind of page this is and to give context for the visual judgment; **do not grade how well the request is fulfilled**):
{query}

## What counts as "clear"

- "Clear" means: **an ordinary person who looks at these side by side once can point it out without thinking hard.** If telling them apart takes repeated comparison or zooming in, it does not count as clear.
- Better and worse are **relative to the group**, not absolute quality:
  - Even if no page in the group is especially good on its own, if one is clearly a bit better than the others, you can put it in good.
  - Conversely, if the whole group is quite good but one or two are noticeably a step below, you can put them in bad.
- Pick too few rather than too many. For each page you pick, you should be able to say in one sentence why it is clearly better or worse than the others.
- **Do not put middling pages into good or bad just to fill the lists.**

## What overall aesthetic impression looks at

Go by overall intuition: does this page look like a finished product made by a mature designer? Does it have a unified visual language, comfortable colors, clear hierarchy, refined assets and appropriate whitespace? Is your first reaction "this looks really good", "usable but ordinary", or "this is bad"?

Do not tick boxes item by item to compute a score. But when comparing within the group, the following **objectively recognized problems** are important references: pages in the group that have these problems are more likely to fall into "clearly worse" than pages that do not.

**Rendering / structure**
- White screen, blank solid-color page, only a skeleton / gray boxes / loading state, content not rendered
- Large parts of the page cut off: edges of images, cards, mockups or charts cut away, especially on dark backgrounds
- Large empty solid-color bands between sections; a block that is half content and half empty
- Elements overlapping each other, text over images and hard to read, content overflowing its container, obvious misalignment

**Assets**
- Crude / badly drawn / out-of-proportion SVG doodles passed off as images (distorted facial features, deformed objects, clip-art feel)
- Placeholder images: gray boxes, broken images, many identical placeholder SVGs repeated
- Generic icons (magnifying glass, star, arrow) blown up huge as the main visual, hollow and cheap
- Almost no visual assets on the whole page, only text plus thin outline boxes, like a docs site or a wireframe

**Text / color**
- Unreadable text: contrast too low, text over busy backgrounds, font sizes out of control
- Glaring, clashing colors, or monotonous and rigid throughout (large areas of pure black / pure white / pure gray, with no layers of color)

"Clearly better" is entirely a subjective feeling: if one page in this group **clearly catches your eye** compared with the others (especially artistic, especially well designed, or especially cool), then it is "clearly better". If you do not have that feeling, do not pick one.

## Notes

- The numbers are anonymous candidates. Do not guess where they come from.
- Do not judge whether the content is correct, whether features work, or whether the request is fulfilled. Other reviewers handle those.
- If several pages are nearly identical (the same template with only small differences), they should go in or out together. Do not split them.

## Output format

Output JSON only, no markdown:
{
  "reason": "First one sentence on the overall level of the group, then the reason for each page picked as good / bad (at most 150 characters)",
  "good": [list of clearly better numbers, may be empty],
  "bad": [list of clearly worse numbers, may be empty],
  "no_clear_difference": false
}

Constraints: good and bad must not overlap; when no_clear_difference is true, good and bad must both be empty; you do not need to cover every number, and any number not listed is "average".

Training dynamics

train reward

0.000.200.400.600.80 0510152023
train reward
stepSol judgeSonnet judge
0 0.610.61
1 0.590.57
2 0.620.62
3 0.620.59
4 0.540.57
5 0.480.60
6 0.400.58
7 0.310.58
8 0.260.60
9 0.190.52
10 0.240.52
11 0.290.47
12 0.320.37
13 0.370.36
14 0.510.36
15 0.560.36
16 0.570.34
17 0.630.49
18 0.650.53
19 0.640.52
20 0.630.60
21 0.630.60
22 0.640.61
23 0.640.64

token entropy

0.300.400.500.60 0510152023
token entropy
stepSol judgeSonnet judge
0 0.370.35
1 0.410.40
2 0.420.41
3 0.450.42
4 0.510.47
5 0.520.49
6 0.560.51
7 0.600.51
8 0.540.54
9 0.570.56
10 0.560.56
11 0.520.54
12 0.570.54
13 0.540.49
14 0.490.44
15 0.460.44
16 0.500.43
17 0.460.43
18 0.390.41
19 0.440.39
20 0.350.41
21 0.360.41
22 0.350.40
23 0.320.38

fulfillment score

0.000.250.500.751.00 0510152023
fulfillment score
stepSol judgeSonnet judge
0 0.880.85
1 0.880.83
2 0.880.87
3 0.880.84
4 0.750.82
5 0.650.83
6 0.570.82
7 0.470.81
8 0.370.83
9 0.330.73
10 0.380.72
11 0.460.67
12 0.470.51
13 0.530.50
14 0.750.51
15 0.810.51
16 0.810.49
17 0.900.68
18 0.910.74
19 0.910.71
20 0.910.82
21 0.900.83
22 0.890.84
23 0.910.88

If you’ve stared at RL plots for any appreciable amount of time, these probably look odd to you. In a healthy RL run, we’d usually expect reward to climb steadily, and for entropy to slowly decline, over the course of training. We see neither of these here. Instead, our final rewards end up around what they were at the start, and entropy rises at first before coming back down near the end of training.

We attribute both of these oddities to our reward design rather than to training going wrong. Recall that our reward is (pick_score + fulfillment + 1)/3\text{(pick\_score + fulfillment + 1)} / {3}, and pick_score∈[−1,1]\text{pick\_score} \in [-1, 1]. pick_score\text{pick\_score} is calculated by asking a model which designs clearly stand out in a group. This means a page that fulfills the brief exactly but doesn’t stand out gets a reward of about 0.67. Also, the judges tend to pick as many “good” pages as “bad” ones. In other words, the average pick_score\text{pick\_score} tends to be near 0.

This is a pretty weird reward function! It’s an ever-moving target. A group of eight equally perfect designs can expect an average reward of 0.67. A group where one page stands out and the other seven are dull averages about 0.71. In other words, unlike a typical GRPO run, mean reward is a poor indicator of whether training is working here. We have to rely on more formal evals.

Similarly, increasing entropy might also be a function of our reward. The relative score here rewards distinctiveness: we are asking the LLM judge to decide which pages are clearly better or clearly worse. So, the pages that stand out from the crowd get rewarded over ones that look like everything else, which encourages a policy that generates more varied designs (and therefore has higher entropy).

Why does reward drop?

Even with the above explanations, it’s still a mystery why reward tanks halfway through training. The fulfillment chart shows that at the halfway point, the average fulfillment score hits around 50% for both models. One cause could be that the average reward is getting dragged down by a wave of pages with zero reward. When this happens, a common cause is context length limits.

generated tokens per rollout

0k40k80k120k 0510152023
generated tokens per rollout
stepSol judgeSonnet judge
0 24.8092k23.2158k
1 30.4314k28.0934k
2 36.0464k33.789k
3 50.773k37.0171k
4 72.9159k46.836k
5 86.0898k51.9949k
6 100.5389k56.902k
7 104.0484k57.2542k
8 101.0849k73.1137k
9 105.0017k85.1206k
10 102.974k91.5288k
11 100.643k98.1378k
12 99.9931k101.1667k
13 98.1522k98.4629k
14 85.9419k96.2911k
15 71.6629k98.3258k
16 71.4292k98.1006k
17 50.3005k96.7325k
18 39.269k94.518k
19 41.5248k91.4086k
20 28.2616k83.1493k
21 29.7772k82.461k
22 26.7088k81.115k
23 21.7207k69.0809k

rollouts with a truncated reply

0%25%50%75%100% 0510152023
rollouts with a truncated reply
stepSol judgeSonnet judge
0 0.4%0.4%
1 1.6%0.8%
2 5.5%5.1%
3 5.1%2%
4 27%7%
5 42.2%7.4%
6 60.5%12.9%
7 69.5%16.4%
8 73%23.8%
9 79.7%43%
10 80.1%54.3%
11 73.4%63.3%
12 64.1%71.9%
13 67.6%72.7%
14 43.8%69.1%
15 22.3%69.9%
16 20.3%65.6%
17 3.9%58.2%
18 2.3%48.8%
19 0.8%52.7%
20 0%32.8%
21 0.4%29.3%
22 0%23.4%
23 0%11.3%

no page delivered

0%10%20%30%40% 0510152023
no page delivered
stepSol judgeSonnet judge
0 0.3906%0%
1 1.1719%2.3438%
2 1.5625%0.7812%
3 1.9531%1.5625%
4 5.0781%1.9531%
5 8.2031%0.7812%
6 16.0156%0.3906%
7 19.5312%2.3438%
8 25.7812%0.3906%
9 33.2031%3.9062%
10 29.6875%6.6406%
11 22.2656%6.6406%
12 21.875%17.9688%
13 13.6719%16.4062%
14 7.8125%23.8281%
15 6.6406%20.3125%
16 1.5625%20.7031%
17 1.9531%13.6719%
18 0.3906%9.375%
19 1.5625%11.7188%
20 0%5.4688%
21 1.5625%3.5156%
22 0.3906%4.2969%
23 0.7812%2.7344%

As expected, most of the fulfillment score drop comes from a spike in rollouts that never deliver a page. Taking a look at the rollout length chart, we can tell this is caused by rollouts growing sharply over training. By step 10, more than half of them have a truncated reply.

Output length creep is common in GRPO runs, (anecdotally) especially with Qwen models. To counter this, many GRPO recipes add a length penalty to keep reasoning under control. The usual culprit is overlong reasoning, but as the charts below show, that’s not the main cause of our long rollouts problem.

characters per rollout

0k50k100k150k200k250k 0510152023 output thinking
characters per rollout
stepSol judge, outputSol judge, thinkingSonnet judge, outputSonnet judge, thinking
0 59.6k16k57.6k13.2k
1 71.5k21.9k66.3k18.9k
2 81.6k27.6k76.5k25.4k
3 111k48.1k82.4k32.1k
4 149.5k77.1k100.5k42.6k
5 163.5k107.3k107.2k54k
6 187.9k129.6k116.6k59.1k
7 187.2k138.1k116.5k58.3k
8 202.8k112.2k138.7k88.5k
9 202k121.8k157k103.6k
10 236.9k92.5k179.4k109.6k
11 216.2k101.5k172.1k130.4k
12 217.4k93.1k187.6k120.8k
13 209.6k98.7k186.3k114.3k
14 188.5k82.8k202.5k89.5k
15 161k66.9k199.4k95.6k
16 162.9k63.7k211.5k83.9k
17 117.9k41.3k202.4k88.7k
18 96.6k23.5k201.7k83.4k
19 100.1k27.6k196.2k75.9k
20 72.9k15.3k173.4k81.4k
21 73.9k17.1k168.6k81.6k
22 67.9k15.6k171.1k76.8k
23 57.3k10.3k146.1k65.5k

median delivered page (chars)

0k50k100k150k200k250k 0510152023
median delivered page (chars)
stepSol judgeSonnet judge
0 41.3k41.7k
1 45.8k44.9k
2 46.2k43.6k
3 64.7k53.9k
4 85.2k65k
5 100.3k69.8k
6 114.4k69.3k
7 119.7k71.1k
8 138.4k86.1k
9 137.3k96.3k
10 143.7k114.1k
11 142.8k122.3k
12 130.5k125k
13 128.9k136.1k
14 110k158.5k
15 92.2k158.8k
16 97.8k156.1k
17 71.4k155.8k
18 55.9k140.4k
19 62.4k147k
20 46.1k118k
21 42.1k114k
22 46.6k121.6k
23 39.6k102k

The growth in reply length largely comes from the model generating longer and longer pages, rather than from overlong reasoning. The most likely explanation here is simply that the LLM judges are biased towards longer designs. This is measurable!

We plot the within-group rank correlation between rollout length and pick_score\text{pick\_score} for each step of training. A positive value means the judge favors longer pages within a group.

length preference of the judge

-0.20.00.20.4 0510152023
length preference of the judge
stepSol judgeSol judgeSol judgeSonnet judgeSonnet judgeSonnet judge
0 0.10.1
1 0.10.2
2 0.20.2
3 0.20.0
4 0.00.1
5 0.30.30.1
6 0.2
7 0.1
8 0.1
9 -0.1
10 0.20.2
14 -0.0-0.0
15 0.00.1
16 0.1
17 -0.0
18 0.0
19 0.1
20 0.10.10.1
21 0.1-0.0
22 0.00.1
23 0.20.1

page height (px)

0k2.5k5k7.5k10k 0510152023
page height (px)
stepSol judgeSonnet judge
0 2.906k2.865k
1 2.886k2.92k
2 3.14k3.007k
3 3.778k3.389k
4 4.123k3.546k
5 4.171k3.525k
6 4.994k3.862k
7 5.472k3.972k
8 5.483k4.118k
9 5.42k4.173k
10 5.516k4.413k
11 6.738k4.571k
12 6.766k5.085k
13 7.566k5.543k
14 6.06k4.998k
15 5.758k5.194k
16 4.9k5.24k
17 5.222k6.005k
18 4.974k5.818k
19 4.45k5.537k
20 4.402k5.29k
21 3.836k4.822k
22 4.257k5.164k
23 4.37k4.738k

This explains the behavior we’re seeing above. In early training, the judges sharply prefer longer pages in a group. This causes the models to rapidly converge on a strategy of generating longer pages. At some point, the long outputs start to go up against max_new_tokens limits and rewards drop sharply. After a few more steps of this, the punishment on “no page delivered” kicks in and corrects the behavior until pages stay at a reasonable length.

If we were to run further experiments, we could try to tackle this by adding a more explicit length penalty earlier on for long pages, or for long rollouts. But as it stands, these models are good enough for us to start evaluating.

Results

To evaluate the models, we run them over a fixed 32-sample subset of the training environments. We care about two things: whether training improves objective design quality over time, and how the choice of judge changes the final model’s design choices.

The explorer below shows how each model’s behavior changes over the course of training.

basestep 4step 8step 12step 16step 20step 24 Sol-trained Sonnet-trained

Training with Sol seems to make the model converge on noticeably “wackier” designs than Sonnet. As an example, step 24 of brief 21 (Agriculture documentation) has more playful fonts, emojis, and background images than the Sonnet-trained counterpart. It looks like a design that went through a few rounds of feedback that said “make it pop more.” At the same time, many of the layouts are broken, laden with clipped or overflowing text.

On the other hand, training with a Sonnet judge makes the model converge on the classic Claude design characteristics: serif display fonts, more restrained and muted color palettes, eyebrow labels above each section heading. Overall, the pages look more editorial, and there is much less variation from brief to brief.

Both models here are seeing a form of mode collapse over the course of training.

Evaluation by pairwise comparison

In subjective domains like design, absolute grades tend to be unreliable. A human grader might rate the same page a 3 one day or a 4 the next, just depending on their mood for the day or what design they saw right before.

So, we start with pairwise comparisons. For each held-out brief, we show a judge two pages side by side and ask it to pick the better design in a blind A/B test. A/B assignments are chosen at random. We use both Sol and Sonnet as judges for every pairing, and report both scores.

Sol-trained vs base

−100%−50%0%+50%+100% 04812162024 even with base
Sol-trained vs base
stepjudged by Soljudged by Sonnet
0 0%0%
4 +3%+13%
8 −75%−77%
12 −56%−54%
16 +56%+41%
20 +38%+33%
24 +44%−4%

Sonnet-trained vs base

−100%−50%0%+50%+100% 04812162024 even with base
Sonnet-trained vs base
stepjudged by Soljudged by Sonnet
0 0%0%
4 +31%+43%
8 +31%+48%
12 −31%−14%
16 −16%−22%
20 +56%+28%
24 +69%+48%

Sonnet-trained vs Sol-trained

−100%−50%0%+50%+100% 04812162024 even
Sonnet-trained vs Sol-trained
stepjudged by Soljudged by Sonnet
0 0%0%
4 +3%+19%
8 +84%+63%
12 +31%+48%
16 −50%−26%
20 +23%+26%
24 +44%+60%

By the end of training, both models’ best checkpoints beat the base model under both judges. Aside from a hard dip in the middle of training when many rollouts break (which we explained earlier), each model’s win rate climbs steadily under its own judge.

Note that even though the final checkpoint of the Sol-trained model has some broken layouts, the Sol judge still prefers its outputs to the base model’s. This makes sense, as those zany-but-broken outputs were trained into it by Sol in the first place.

This intuition breaks in the Sol-trained vs Sonnet-trained grading jobs, though. When the two models face each other, both eval judges strongly prefer the Sonnet-trained model: Sol rates it +44% over the Sol-trained model, and Sonnet rates it +60%. If anything, we would have expected Sol to prefer the Sol-trained model. Instead, the Sonnet-trained model leads for most of training, with the exception of checkpoint 16.

The explorer below shows every head-to-head verdict along with screenshots of the pages and each judge’s reasoning.

Comparing best checkpoints

We may also just be catching the Sol-trained model at a bad moment. It peaks against the base model at step 16 and is degraded according to Sonnet judge by step 24. A fairer test would be to compare each model’s best checkpoints directly. To rule out checkpoint selection as a cause for our unintuitive evaluation results here, we run one more head-to-head between Sol-trained and Sonnet-trained models at their best checkpoints (steps 16 and 24, respectively).

Under these settings, the Sonnet-trained model still beats the Sol-trained model.

JudgeSonnet-trained winsSol-trained winsTiesSonnet-trained net win rate
Sol2390+44%
Sonnet18130+16%

Why does the Sol-trained model lose?

I don’t quite have a good explanation for the eval results we’re seeing here, but I can offer two hypotheses:

  • Sol might just not be a good judge for a group pick judge task. In training, we’re asking the judge to look at 8 full-page screenshots at once and pick out clear winners and losers. Especially on long pages, that’s a lot of image to reason over.
  • The group pick task also inherently rewards standing out. The system prompt asks which pages are clearly better or worse than the rest of a group, and pages that look different from the rest are the easiest to pick. This means that over the course of training, the judge may be reinforcing tics that catch its eye, even if they don’t all add up to a clearly better design.

In any case, these results do illustrate one of the key limitations of relative grading: although it can tell us which page is better, it can’t tell us anything about why. It also hides the true underlying quality a bit. For example, a comparison between a Qwen3 4B model and Qwen3 0.6B model might show that the 4B model wins in 100% of cases, but in the end the 4B model’s outputs are probably still too bad to use on a real website.

Evaluation against a rubric

To study what’s actually improving in the models over the course of training, we turn to the rubric-based judge Xiaomi shipped along with the environments.

The judge grades page outputs along seven different axes:

AxisGroupWhat the judge looks for
Layout integrityvisualNothing overlaps, overflows or gets cut off.
Typography hierarchyvisualHeadings, body and small text are clearly distinct and easy to read.
Color harmonyvisualA coherent palette with readable contrast.
WhitespacevisualSpacing that is neither cramped nor empty.
Content richnessvisualEnough real content for a full site, not an empty shell.
Query fulfillmentbriefHow completely the page does what the brief asks.
Premium assetsassetsQuality imagery and illustration, not emoji or placeholders.

The final score is the average of the fulfillment score, premium assets score, and the mean of the visual group scores.

The rubric judge prompt
You are a web UI reviewer. Score only on the evidence visible in the full-page screenshot. The first 5 items make up visual: the first four assess only objective visual problems, and the fifth assesses the amount of content a complete website needs; item 6 is judged against the query; item 7 assesses the quality of the assets. Judge each dimension independently. Do not let strong or weak performance on one dimension change the others.

query: {query}

Output a continuous score from 0 to 1 for each dimension; any decimal is allowed. First use the five-band description of the dimension to decide which band the score falls in, then pick a value within the band according to the severity, number and impact of the problems; do not output only band endpoints or fixed levels. The five bands are always [0.0,0.2), [0.2,0.4), [0.4,0.6), [0.6,0.8), [0.8,1.0]. In borderline cases, choose the adjacent band that best matches the visible evidence. premium_assets must not enter [0.6,1.0] when there are no premium assets.

1. layout_integrity (layout integrity)
- [0.0, 0.2): The layout is badly broken; core content overlaps, is cut off, overflows or is unreadable over large areas
- [0.2, 0.4): Several obvious layout errors that significantly hinder use of the main content
- [0.4, 0.6): The main body is usable, but there is one serious or several obvious alignment, overlap or overflow problems
- [0.6, 0.8): Complete overall, with only a few minor alignment, spacing or boundary problems
- [0.8, 1.0]: The layout is complete and stable, with no perceptible overlap, misalignment, cut-off or overflow

2. typography_hierarchy (typographic hierarchy)
- [0.0, 0.2): Text is generally unreadable; font sizes and hierarchy are badly out of control
- [0.2, 0.4): The hierarchy between headings and body text is confused; font size, line height or weight is clearly wrong in several places
- [0.4, 0.6): Basically readable, but there are obvious flaws in how distinguishable the hierarchy is or in the typographic rhythm
- [0.6, 0.8): Clear hierarchy and smooth reading, with only a few minor typographic problems
- [0.8, 1.0]: Headings, body and supporting text form a distinct hierarchy, with mature proportions, line heights and weights

3. color_harmony (color and contrast)
- [0.0, 0.2): Colors clash badly, or key text is almost impossible to make out
- [0.2, 0.4): Many uncoordinated colors or weak contrast that clearly hurt the look or readability
- [0.4, 0.6): Basically usable, but the color system, contrast or consistency has obvious problems
- [0.6, 0.8): Colors are coordinated and readable, with only a few minor inconsistencies or contrast problems
- [0.8, 1.0]: A unified, mature color system with clear contrast and no obvious problems

4. whitespace (whitespace and density)
- [0.0, 0.2): The page is extremely crowded or empty; the organization of information basically fails
- [0.2, 0.4): Many places are clearly too dense or too sparse, badly breaking the browsing rhythm
- [0.4, 0.6): Browsable overall, but local density, spacing or section rhythm has obvious problems
- [0.6, 0.8): Whitespace and content density are reasonable overall, with only a few minor problems
- [0.8, 1.0]: Whitespace, spacing and section rhythm are natural and consistent; density is handled maturely

5. content_richness (content richness)
- [0.0, 0.2): Almost an empty shell that does not fill one screen, with only a title, an empty frame or very little placeholder content
- [0.2, 0.4): Only 1-2 simple sections; content is highly repetitive, vague or mostly placeholder
- [0.4, 0.6): Several real sections, but clearly not enough in number or depth to support a complete website
- [0.6, 0.8): Sections are fairly complete and content is fairly specific; only a few parts are thin
- [0.8, 1.0]: About 4 or more fully formed sections, with specific, substantial content, enough to support a complete website experience
6. query_fulfillment (requirement fulfillment)
- [0.0, 0.2): The visible content basically does not implement the query, or the theme is completely wrong
- [0.2, 0.4): Only a few surface elements are implemented; most core requirements are missing
- [0.4, 0.6): The main direction is implemented, but several key sections, elements or functional interfaces are clearly missing
- [0.6, 0.8): The main requirements are implemented, with only a few minor omissions or deviations
- [0.8, 1.0]: Within what the screenshot shows, the query's theme, sections, elements and functional interfaces are implemented completely and accurately

7. premium_assets (premium assets and finish)
- [0.0, 0.2): No usable visual assets, or relies mainly on crude emoji, bare placeholders, or low-quality or blurry images
- [0.2, 0.4): Assets are ordinary or rough and lack a unified design sense; a little decoration does not create a sense of quality
- [0.4, 0.6): Assets are clear and basically coordinated, but are mainly ordinary images or pure-CSS visuals, with no premium assets
- [0.6, 0.8): Uses high-quality real photos, artistic images, a systematic set of illustrations or refined mockups; good overall quality, but still with small flaws
- [0.8, 1.0]: Premium assets of outstanding quality, forming a complete system and closely integrated with the layout, reaching the level of mature professional work

If the page is blank, badly fails to load or is completely invisible, output 0 for all dimensions. Output only JSON, no markdown:
{{"layout_integrity":0.0,"typography_hierarchy":0.0,"color_harmony":0.0,"whitespace":0.0,"content_richness":0.0,"query_fulfillment":0.0,"premium_assets":0.0,"reason":"main evidence"}}

To get the scores below, we grade every page twice, once with Sol and once with Sonnet, and report the average.

AxisBaseSol-trainedSonnet-trained
Overall64.566.870.6
Visual quality (mean)77.376.981.2
Layout integrity73.164.776.6
Typography hierarchy78.278.682.3
Color harmony77.280.380.2
Whitespace75.872.178.1
Content richness82.088.688.7
Query fulfillment80.684.584.1
Premium assets35.539.246.4

rubric score, change from base

−20−100+10+20 base Overall (mean)Visual quality (mean)layout integritytypography hierarchycolor harmonywhitespacecontent richnessQuery fulfillmentPremium assets
Sol-trained, step 24Sonnet-trained, step 24

We see a similar trend here as we did with the relative grading results. Even though both models improve on the base model, training with a Sonnet judge makes for a better overall model according to both judges.

Notably, the Sonnet-trained model’s biggest gain is in premium asset usage, which rises far more under Sonnet than under the Sol judge. A quick flip through the page explorer shows why: the final Sonnet-trained checkpoint tends to draw custom illustrations for its card and hero sections, whereas the Sol-trained model leans heavily on emojis.

The rubric scores also show us why the Sol-trained model loses ground: by the end of training, the layout integrity of the model’s outputs are -8.4% on average against the base model. So while the Sol models generates more “wacky” designs in the end, this comes at the expense of the model’s ability to actually emit coherent designs in that style.

Take the three briefs below for example. The Sol-trained versions of each output score significantly lower in layout integrity than the Sonnet-trained counterparts. Yet, to me personally, the Sonnet-trained outputs are rather safe and bland. Each page’s layout integrity score is reported in the bottom-left of its screenshot.

Base · avg layout integrity 85.7

Sol-trained · avg layout integrity 43.7

Sonnet-trained · avg layout integrity 72.7

Top of the base model cyberpunk coworking cafe page, layout integrity 78 78
Top of the Sol-trained cyberpunk coworking cafe page, layout integrity 40 40
Top of the Sonnet-trained cyberpunk coworking cafe page, layout integrity 60 60
Top of the base model retro diner campaign page, layout integrity 90 90
Top of the Sol-trained retro diner campaign page, layout integrity 42 42
Top of the Sonnet-trained retro diner campaign page, layout integrity 84 84
Top of the base model art deco moving company page, layout integrity 89 89
Top of the Sol-trained art deco moving company page, layout integrity 49 49
Top of the Sonnet-trained art deco moving company page, layout integrity 74 74
Layout integrity score reported with each example.

This leaves us in a pretty interesting position: training with Sol leads to less collapse to the same design language. This is definitely debatable, though. If I have the wrong read here, feel free to let me know! I am not a designer, so my tastes are immature in this area. On the other hand, while a Sonnet judge is much better at catching design mistakes, it also causes our model to collapse onto the same generic design language that Claude is known for.

How could we design a better judge?

In our experiments here, we’ve tried to reproduce Xiaomi’s setup as faithfully as we could. But if we were to try designing a better judge on our own, what would we do?

The core problem in the current relative grading setup is that it mixes together two different ideas of what it means for a design to be good. The first is objective: is the page well-built, does it follow basic design principles, is anything in the page obviously broken or cut off? The second is the subjective question of whether the page actually looks good. A judge asked to pick the “better” pages in a group weighs both at once and, as we saw in our experiments, can end up rewarding one more than the other.

That second question is harder to hand off reliably to an LLM judge because it’s a matter of taste. To my knowledge, no LLM out there is truly aligned with designers on design judging. In my view, it’s an intractable problem and we are better off with many models here, one each for a different designer’s tastes. Even professional designers often disagree on which of two designs is better. Contra found in their studies that even among human designers, inter-designer agreement on pairwise grading is as low as 60%.

Still, a better reward would at least separate the two explicitly:

  1. An absolute judge for the objective half, scoring each page on a number of different properties such as layout integrity and typography choice along a rubric.
  2. A relative judge for measuring taste. This could either be the existing group pick judge or a pairwise tournament scheme. We should only admit pages above an absolute threshold score here, or we risk rewarding a structurally broken page just because it looks striking.

While we’re here, we can even throw in a reward term that punishes common AI design tells to avoid the kind of Claude-style mode collapse we saw with the Sonnet-trained model.

I plan to explore all this in a future blog post. Stay tuned!

Bonus: Does it generalize?

Recall that goal was to see how RL shapes a model’s design taste. So far, though, our evals have only looked at the same web development style tasks the models were trained on. A fair questions is whether the training actually changed the models’ tastes, or only taught them how to behave inside of one environment. That is — does the trained behavior generalize?

To test this, we had each model design two of the eval briefs directly in Figma, using an Opencode harness and the Figma MCP server. We also explicitly turn off the html.to.design tool and ask the models to use plugin code to create the designs.

We leave the question of generalization to the reader. Flip between “Figma” and “Web page” to judge for yourself. Just one bit of commentary: the models do seem, unfortunately, limited by the expressivity of Figma plugin code as opposed to html here.

Base

Base model's Figma design, Growfield: sans-serif headline, flat landscape illustrations, complete cards and a contact form

Sol-trained

Sol-trained model's Figma design, Harvest&Co: heavy green display serif, orange pill buttons, near-empty cards and clipped text

Sonnet-trained

Sonnet-trained model's Figma design, Sow & Signal: serif display with an italic accent, eyebrow labels and a hero stat row

Base

Base model's Figma design, COLD AID: bold sans, red and black on white, strict grid, data panel

Sol-trained

Sol-trained model's Figma design, COOL AIR CLUB: huge headlines, bright video tiles, clipped text and overlapping pledge cards

Sonnet-trained

Sonnet-trained model's Figma design, VOSS IX: bold sans headline with a red dot, a red stats card, a dark pledge board and field notes; the design stops partway down the page

[hit context limit]