7.8 KiB
name, description
| name | description |
|---|---|
| house-hand | Draw characters, mascots, icons and scenes in the House Hand style with house.py, check them against its rules, and hand back an SVG, a PNG and a version that draws itself. Use when someone asks for a drawing in the House Hand, or for illustrations that should match the drawings at househand.yolopreneur.lol. |
House Hand
House Hand is a drawing style written down as code. Every drawing is an SVG built from a few pieces in house.py:
ink outlines of one weight, round tubes for limbs, flat colours from a small palette, and hard-edged shadows. You draw
by writing a short Python script that calls the library. Then you render it, look at it, and fix what's wrong.
Everything you need is at https://househand.yolopreneur.lol/:
house.py: the drawing library. Python 3.9 or later, standard library only.drawon.py: turns a finished SVG into one that draws itself, stroke by stroke.skill.md: this file.
curl -O https://househand.yolopreneur.lol/house.py -O https://househand.yolopreneur.lol/drawon.py
A first drawing
Save this as draw_wave.py next to house.py and run it:
from house import *
W, H = 640, 800
someone = figure(320, 720, view="front", shirt=MINT, pants=SKY, shoes=YEL, hair=(PINK, "cap"),
arms=(None, (4.6, -2.6)), hands=("open", "wave"), ground_shadow=True, uid="someone")
fx = sparkle(540, 170, 22, YEL) + sparkle(540, 170, 9, WHITE)
svg = (f'<svg xmlns="http://www.w3.org/2000/svg" width="{W}" height="{H}" viewBox="0 0 {W} {H}">'
f'<rect width="{W}" height="{H}" fill="{CREAM}"/>{someone}{fx}</svg>')
open("wave.svg", "w").write(svg)
That is a whole character waving hello: head, beanie, sleeves with cuffs, trousers, shoes, shadows and a sparkle.
Start every drawing from figure() and change one thing at a time.
The rules
Line and colour
- One ink line everywhere:
INK(#2B2135),LW= 10 px wide, round caps and round joins. A tube is an ink strokew + 2·LWwide under a colour strokewwide on the same path (tube()). - Fill only with the palette:
PINK YEL ORANGE MINT SKY CREAM SKIN LAV WHITE, or a shadow of one of them made withshade(colour): the same hue, more saturated, a little darker. Never grey, never a colour from outside the palette. - Shadows and effects have no outline. One hard-edged shadow shape per form, clipped to that form with a
<clipPath>(give shadow shapesclass="s"and effectsclass="fx"). On a tube, useband(). - No gradients, filters, blur, raster images or thin detail strokes. Seams, veins and speed lines are filled shapes.
- Escape
&in text as&.
Shapes
- Corners and tips are rounded. Straight edges bow a little (2–5%). Use as few anchor points as the shape needs.
- The head is an oblong 1.2 times taller than wide, flattened top and bottom (
head_path). Eyes sit on the upper third, the mouth centred just below. In a three-quarter view both eyes stay inside the silhouette and the far eye is narrower. Ears are closed lobes. - The torso is a bean: a belly circle and one arced back (
bean_path). - Adults are 4.5 to 5 heads tall, children and big-headed characters 3 to 3.5. Legs are about half the height.
- Limbs are single curves of constant width. Never an angular elbow or knee.
- Hands are three fingers and a thumb. The thumb is 1.15 times a finger's width, shorter, and low on the palm. Feet have four toes.
- Every garment opening (cuff, hem, collar) is an ellipse with a lip (
opening()). Sleeves and trouser legs are 1.6 to 1.7 times the limb (sleeve()). - Detail matches size: a small thing is a silhouette plus one mark.
- No tangents. No contour grazing another, no shape sitting exactly on a horizontal line (a rim, a horizon, the edge of the sheet). Overlap clearly or leave a gap.
- Draw originals. Don't copy an existing character, logo or another artist's signature devices.
The library
figure(cx, ground, ...) draws a whole character standing on ground, centred on cx. The arguments you will use
most:
| argument | what it does |
|---|---|
head_h, heads |
head height in px (default 110) and height in heads (default 4.7) |
view |
front, q and qr (three-quarter left and right), side, sider, qb, qbr, back |
skin, shirt, pants, shoes |
colours from the palette |
hair |
(colour, kind) with kind tuft, cap, bob, bun, spikes or none |
garments |
volume (sleeves and trousers with cuffs, the default), skin or none |
skirt, legwear |
a colour for a flared skirt; legs then come out of its hem in legwear |
arms, legs |
((dx, dy), (dx, dy)): where each hand or foot goes, in limb widths from the shoulder or hip; None hangs |
hands |
a pose per hand: open, point, fist, peace, pinch, thumbsup, grip, wave |
props |
per hand, an SVG string or a function (x, y, rot, fw) that draws what the hand holds (use with grip) |
eyes, mouth_kind |
eyes dot, wide, closed, happy, flat; mouth smile, flat, small, o, open, grin, smirk, frown, none |
accessories |
any of glasses, scarf, bow, badge |
lean, head_dx, head_dy |
the line of action: torso lean and head offset |
under, mid, over |
functions (g) for parts behind everything, under the torso, or on top (ears, tails) |
ground_shadow |
a soft shadow under the feet |
uid |
a unique id per figure, so clip paths don't collide |
The pieces it is built from are there to use on their own: head, eye, mouth, hand, shoe, bare_foot,
tube, sleeve, band, opening, blob, outline, line, flat, ellipse_path, limb_curve, cpath,
and the effects sparkle, drop, flame, splash, lobes (clouds and steam). Read their docstrings in house.py.
Never scale a figure by its arguments to make it smaller: the line weight is fixed, so the ink would get heavy. Draw at
full size and wrap it in <g transform="scale(0.5)">. Keep heads at least about 70 px tall on a 1600×1000 sheet, or
cuffs and shadows stop reading.
How to work
- Write
draw_<thing>.pythat doesfrom house import *and writes an SVG. Oneuidper figure. - Render it at 1×, for example with headless Chrome or Chromium:
chromium --headless=new --hide-scrollbars --window-size=640,800 --screenshot=wave.png file://$PWD/wave.svg(or with Playwright, orrsvg-convert -o wave.png wave.svg). - Look at the PNG yourself before calling it done. Check the rules above one by one: line weight, palette, hands and feet, openings with lips, tangents. Collisions are obvious to the eye.
- Fix the weakest thing and render again. Change one thing at a time.
- Hand back the SVG and the PNG.
A drawing that draws itself
HOUSE_TAGS=1 python3 draw_wave.py # tags each part, so characters are drawn head first
python3 drawon.py wave.svg wave-draws.svg # [--budget 12] caps the line work, in seconds
python3 drawon.py wave.svg wave-draws.svg --preview wave.mp4 # also an MP4 and a GIF (needs Playwright and ffmpeg)
wave-draws.svg replays the drawing: every ink line at one pen speed, then the flat colours, the shadows and the
effects. It is plain SVG animation with no script, so it plays anywhere an SVG shows, including an <img> tag. It
also plays in an ordinals explorer once inscribed. Inscribe it compressed
(ord wallet inscribe --fee-rate N --file wave-draws.svg --compress): a self-drawing SVG often shrinks to about a
tenth of its size, and the fee with it.
Animation
For a loop, write the scene as frame(t) returning a whole SVG. Ease with small back-eased pops, rising wipes and
sine bobs; keep the face the same across the loop. Render the frames with Playwright, then
ffmpeg -framerate 24 -i f%04d.png -vf format=yuv420p -c:v libx264 -crf 18 loop.mp4 for video, or palettegen and
paletteuse for a GIF.