| name | hand-draw |
| description | 100% hand-drawn image generation with Korean Hangul support. Zero font files โ all characters rendered from manually-defined stroke primitives. Produces notebook-style illustrated cards with wobble effects. Actions: ์๊ทธ๋ฆผ, ์๊ธ์จ ์ด๋ฏธ์ง, hand-draw, ์ผ๋ฌ์คํธ ๋ง๋ค์ด, ์ด๋ ์ด๋ฏธ์ง |
Hand-Draw โ 100% Font-Free Korean Illustration Engine
Generate hand-drawn style images with Korean text, stick figures, and decorative elements.
No font files used at all โ every character (ํ๊ธ + ASCII) rendered from stroke-level primitives.
When to Use
- User requests hand-drawn / ์๊ธ์จ style images
- Exercise cards, study notes, cute illustrations
- Annotated diagrams with Korean + English text
- Any image where handwritten aesthetic is desired
Architecture
scripts/
โโโ hangul_render.py # Core: Hangul jamo stroke renderer + text API
โโโ draw_utils.py # Wobble primitives, backgrounds, decorations
examples/
โโโ exercise_images.py # Full example: 6 exercise illustration cards
Core Module: hangul_render.py
How It Works
- Jamo Stroke Database: All 19 initial consonants (ใฑ-ใ
) and 21 medial vowels (ใ
-ใ
ฃ) defined as normalized polyline coordinates
[(x, y), ...] in 0.0-1.0 space
- Unicode Decomposition:
decompose_syllable('ํ') โ (ใ
, ใ
, ใด) via Unicode math: code = 0xAC00 + (initialร21 + medial)ร28 + final
- Vowel Layout Detection:
get_vowel_type() โ 'right' (ใ
ใ
ใ
ฃ), 'bottom' (ใ
ใ
ใ
ก), 'compound' (ใ
ใ
)
- Syllable Block Composition: Positions initial/medial/final jamo within bounding box based on vowel type
- Wobble Rendering: All strokes subdivided into segments with random displacement for hand-drawn effect
Key API
from hangul_render import hand_write, hand_write_centered, hand_write_multiline
img = Image.new("RGB", (600, 200), (255, 252, 245))
draw = ImageDraw.Draw(img)
hand_write(draw, x=20, y=30, text="์๋
! Hello 123",
color=(60, 60, 65), size=50, width=2, wobble_amt=1.5)
hand_write_centered(draw, cx=300, cy=100, text="๊ฐ์ด๋ฐ ์ ๋ ฌ",
color=(225, 60, 75), size=40)
hand_write_multiline(draw, x=20, y=50, text="์ฒซ์งธ ์ค\n๋์งธ ์ค\n์
์งธ ์ค",
color=(60, 60, 65), size=30, line_spacing=1.4)
Parameters
| Param | Type | Default | Description |
|---|
| draw | ImageDraw | required | PIL ImageDraw object |
| x, y | int | required | Starting position |
| text | str | required | Korean + ASCII mixed text |
| color | tuple | required | RGB color, e.g. (60, 60, 65) |
| size | int | required | Character height in pixels |
| width | int | 2 | Stroke width |
| wobble_amt | float | 1.5 | Randomness for hand-drawn feel |
Special Characters Supported
- Korean: All 11,172 syllable combinations (๊ฐ-ํฃ)
- ASCII: A-Z, a-z, 0-9
- Symbols:
! ? . , : ; / + - = * # @ % & ( ) [ ] ~ โ โ โ โ " ' _ โก โ
โช
Utility Module: draw_utils.py
Primitives
from draw_utils import wobble_line, wobble_rect, wobble_ellipse
wobble_line(draw, x1, y1, x2, y2, color, width)
wobble_rect(draw, x1, y1, x2, y2, color, width)
wobble_ellipse(draw, cx, cy, rx, ry, color, width)
Decorations
from draw_utils import hand_arrow, hand_heart, hand_star, hand_checkmark, hand_cross
hand_arrow(draw, x1, y1, x2, y2, color, width)
hand_heart(draw, cx, cy, size, color)
hand_star(draw, cx, cy, size, color)
hand_checkmark(draw, cx, cy, size, color)
hand_cross(draw, cx, cy, size, color)
hand_underline(draw, x, y, width_px, color)
Backgrounds
from draw_utils import draw_paper_bg, draw_grid_bg, draw_dot_bg
draw_paper_bg(img)
draw_grid_bg(img)
draw_dot_bg(img)
Stick Figure
from draw_utils import stick_figure
stick_figure(draw, cx=200, cy=150, scale=1.2,
arm_angle=45, leg_angle=30)
Color Palette
from draw_utils import PALETTE
PALETTE['bg']
PALETTE['body']
PALETTE['accent']
PALETTE['accent2']
PALETTE['green']
PALETTE['orange']
PALETTE['pink']
PALETTE['purple']
PALETTE['machine']
Dependencies
- Python 3.8+
- Pillow (
pip install Pillow)
- No font files needed (that's the whole point!)
Example Usage
cd ~/2lab.ai/skills/hand-draw
python examples/exercise_images.py /tmp/my-output
python scripts/hangul_render.py
Design Philosophy
"ํฐํธ ์ฐ์ง ๋ง๊ณ ์ง์ ์จ์ค" โ Every character is drawn stroke-by-stroke.
All text rendering happens through manually-defined stroke coordinates:
- Each consonant/vowel has its own polyline definition
- Unicode syllable decomposition handles ๊ฐ-ํฃ automatically
- Wobble + jitter make each rendering unique
- No
.ttf, .otf, or any font file is ever loaded
Tips for Agents
- Always
random.seed(N) before generating for reproducible results
size=30-50 works well for annotations, size=20-25 for small notes
width=3 for bold/emphasis, width=2 for normal text
- Use
draw_paper_bg() first, then create ImageDraw.Draw(img) for content
- Mix
hand_write with hand_heart / hand_star for cute decorative style