File size: 5,311 Bytes
a2ce6f2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# Field Notes: Cross-Page Character Consistency in DoodleBook

*How we keep the same hand-drawn hero across all six pages of a storybook — from a single child's doodle, with no per-user training — using FLUX.2-klein, a canonical-character pass, seed-locking, and a fixed character description.*

---

## The Challenge

The core problem in AI-generated storybooks is **character consistency**. Generate six pages independently and you get six different characters — different colors, proportions, and style on every page. The magic is gone.

We needed: **the same character, in the same crayon style, across all six pages — and it has to look like the picture the child actually drew.**

A tempting answer is "train a LoRA per child." That is infeasible at demo time (LoRA training is minutes-to-hours on a real GPU), so we deliberately did **not** rely on per-user training. Instead, character identity comes from the child's own drawing, carried through the book by image conditioning.

---

## Our Approach: The Consistency Stack

We layer four complementary techniques. None solves it alone; together they're reliable.

### 1. The canonical-character pass (the real secret sauce)

This is the key idea. We do **two stages**:

1. **Stage 1 — build the canonical hero.** The child's doodle is sent through FLUX.2-klein **img2img** once, turning the rough drawing into one clean, full-body crayon character on a plain background. This is the "model sheet" for the whole book.
2. **Stage 2 — render every page from that canonical.** Each of the six scenes is rendered with the **canonical image itself as the reference** (`image=canonical`), plus the scene description. Because every page is conditioned on the *same* reference image, the hero stays recognizably the same creature the child drew.

```python
canonical = build_canonical(doodle)            # doodle -> one clean hero (img2img)
pages = [render_page(canonical, scene, seed+i)  # every page references the SAME hero
         for i, scene in enumerate(scenes)]
```

Identity comes from the **child's drawing**, not from a trained checkpoint — so a cat doodle becomes a cat hero, a robot becomes a robot, with no training step.

### 2. Seed locking

```python
BASE_SEED = 42
# page i uses BASE_SEED + i + 1  -> reproducible, slight per-page variation
```

Deterministic seeds make a book reproducible (same inputs → same outputs) while still letting each page differ.

### 3. A fixed character description

The same `character_description` string is reused on every page as a text anchor (and is the fallback identity when no doodle is uploaded, via text2img).

### 4. Style in the prompt

A consistent crayon art-style suffix ("hand-drawn crayon storybook illustration, waxy crayon texture…") keeps the medium uniform across pages.

---

## The Coloring Book: redraw, don't trace

A matching coloring book is the second output. Our first attempt **traced** the finished crayon pages with classical edge/region detection — but crayon texture shattered busy backgrounds into speckle, producing pages no child could color.

The fix: hand each finished color page back to FLUX as **img2img** with a "clean black-and-white coloring-book line art" prompt. FLUX understands the scene semantically and redraws clean shape outlines that *match* the story page, instead of tracing texture. A tiny local pass then thresholds it to crisp black-on-white.

---

## Key Learnings

1. **Seed alone isn't enough.** Same seed + different prompt still drifts. You need an identity anchor.
2. **Image conditioning beats per-user training.** Feeding the child's actual drawing (as a canonical reference) gives per-character identity *without* training a LoRA per user — the decisive call for a live demo.
3. **For line art, redraw beats trace.** Generating coloring pages with a model that understands the scene is far cleaner than stripping color out of a finished texture.
4. **4B is the right renderer.** FLUX.2-klein-4B runs fast enough for a live book with strong storybook quality.

---

## Technical Details

**Model stack**
- Image / renderer: `black-forest-labs/FLUX.2-klein-4B`
- Story / brain: `openbmb/MiniCPM5-1B` (1B)
- Voice: `openbmb/VoxCPM2` (2B)

**Inference (storybook pages)**
```yaml
guidance_scale: 1.0
num_inference_steps: 6
width: 768
height: 768
```

**Coloring pages**: FLUX img2img from the color page, `strength≈0.85`, then threshold/despeckle.

---

## Roadmap: a crayon-style LoRA (not yet trained)

A natural next step is a **crayon art-style LoRA** on FLUX.2-klein to make the medium even more uniform and reduce prompt sensitivity. The training pipeline is scaffolded, but **no LoRA has been trained or published yet**, so we make no "Well-Tuned" claim — current consistency is achieved entirely by the canonical-character + seed + description stack above.

---

## Conclusion

Character consistency in AI storybooks doesn't require per-user fine-tuning. A canonical-character pass (drive every page from one img2img render of the child's own drawing), a locked seed, and a fixed description together turn a single crayon doodle into a consistent, narrated, illustrated book — the child's character, their style, brought to life.

---

*Built for Build Small Hackathon 2026 · Adventure in Thousand Token Wood*