Proportions & Shape Accuracy
- The artist mirror technique. Flipping both the drawing and the reference simultaneously reveals proportional errors your brain has adapted to. Use
transform="translate(w,0) scale(-1,1)". - Dominant colour test. Step back and ask which colour should be the largest area. If it doesn't match what you see, fix the boundary paths before touching anything else. Orange breast should be ~55% of a flat-design robin's body area; if the wing looks dominant, the wing path is too wide.
- Separate circle + ellipse always looks disconnected. The real fix is a single
<path>for the whole silhouette, with colour regions overlaid on top. Short-term seam fix: place the wing path's anchor inside the head circle so the head (drawn later) covers the gap. Test:dist(wing_start, head_center) < head_radius.
SVG Filters & Texture
- feTurbulence for any organic pattern — feathers, pan-fry marks, bark, cloth. Hand-placed ellipses always look artificial.
Recipe:<feTurbulence baseFrequency="0.038 0.028" numOctaves="3" seed="11"/>→<feColorMatrix values="0 0 0 0 0 / 0 0 0 0 0 / 0 0 0 0 0 / 8 0 0 0 -3.2"/>→<feComposite operator="in"/>. Lower baseFrequency = bigger blobs; higher alpha multiplier = sharper edges. Asymmetric frequency (0.042 H, 0.024 V) makes horizontal streaks — good for cooked food textures. operator="in"composite reveals the source fill colour where the alpha mask fires — the source rect/ellipse fill must already be the desired output colour.- Filters can bleed outside clipPaths if the filter region (x/y/width/height) is too large. Always constrain.
Gradients & Colour
- Radial gradient = sphere. Never use a centre-bright radial gradient on a flat surface — it reads as a bubble. Use linear for flat surfaces.
- Atmosphere tricks: vignette (
radialGradienttransparent→black(0.55) as topmost<rect>), warm glow halo (large blurred amber ellipse at opacity 0.07–0.10 under the subject), wood grain (two sets of near-invisible diagonal lines at different angles, opacity 0.05–0.06).
Z-Order & Masking
- SVG's painter model is a free masking tool. Draw the yellow belly before the dark wing; the wing covers most of it and leaves a visible crescent — no
clipPathneeded when overlapping shapes share a colour. - Never draw a shape twice for masking. Drawing the wing twice to "mask off" the belly doubled coverage and hid the orange breast entirely. Each shape exactly once; rely on z-order.
Bezier Primitives bezier-bird
- Cubic bezier approximation of a circle: k = 0.5523. Any
<ellipse>(cx, cy, rx, ry) becomes four cubic bezier segments:M cx,cy-ry C cx+k·rx,cy-ry cx+rx,cy-k·ry cx+rx,cyC cx+rx,cy+k·ry cx+k·rx,cy+ry cx,cy+ryC cx-k·rx,cy+ry cx-rx,cy+k·ry cx-rx,cyC cx-rx,cy-k·ry cx-k·rx,cy-ry cx,cy-ry Z - Parameter cascade: changing one shape forces updates downstream. Reducing body ry changes body-bottom y → leg-start y → foot y → shadow y. Work top-to-bottom in one pass.
- Head-to-body width ratio ≈ 30–35% for a flat geometric bird. r=70 on a 500px canvas is 47% (too big); r=50 is 34% (correct). Body aspect ratio for a perched bird: rx/ry ≈ 1.8–2.0 (a circular body reads as a ball).
Triangle Tessellation triangle-bird
- Fan tessellation (centroid → consecutive perimeter pairs) covers any roughly convex region. Strip tessellation (quad columns between top/bottom polylines, split diagonally) covers band-shaped features. Use fan for body, wing, head, tail; strip for the blue-gray accent.
- Fan centre placement matters. Near the geometric centre minimises the longest spoke and avoids thin slivers at the far edge. The centre becomes the visual focal point of all seam lines.
- Triangle seam lines are inherent. Adjacent same-coloured triangles produce sub-pixel anti-aliasing gaps — visible at 2×+ zoom, negligible at 1×. Accept as an aesthetic feature.
- Pixel scan, then draw. Use
canvas.getImageData: topmost-y per x (back arch), rightmost-x per y (breast), color-specific per row/column (tail, blue-gray strip), angular radial sweep (head boundary). Each scan type suits a different feature geometry. - Line-art calibration overlay before the triangle file. Separate HTML with
viewBoxmatching the photo's native resolution; overlay coloured strokes on the photo and iterate until every line sits correctly. Scale all coordinates once at the end (500/1920 = 0.2604).
Bezier Stroke Outlines bezier-outline-bird
fill="none"on every path is a complete drawing technique. Coloured strokes alone produce readable, expressive line art — the viewer infers the colour regions from the boundary lines.- Colour-code strokes by region, not by outline role. Use the region's own colour as the stroke (orange for body, dark navy for head, orange-red for tail) rather than a neutral black. Open paths work as boundary markers without enclosing any area.
- Catmull-Rom to cubic bezier gives smooth curves through scanned points. For segment Pᵢ→Pᵢ₊₁:
CP1 = Pᵢ + (Pᵢ₊₁ − Pᵢ₋₁)/6,CP2 = Pᵢ₊₁ − (Pᵢ₊₂ − Pᵢ)/6. Mirror the missing neighbour at endpoints (P₋₁ = P₀, Pₙ₊₁ = Pₙ). - Fill toggle via
data-fill. Adddata-fill="colour"to closed paths; JS swapsfillbetween the stored colour and"none". Open paths are excluded automatically — they have no attribute to read. Close a V-shaped path with Z before assigningdata-fillor the fill has no area to paint.
Pixel Scanning & Automation scanner
- No single colour region yields the full outer silhouette. When features overlap (wing sits on top of body), the orange body scan misses the back arch entirely — it's hidden under the wing. The outer silhouette must be assembled from multiple region boundaries: orange H-scan for breast and belly; dark-navy V-top for the back arch (the wing's top edge is the bird's outer back).
- H-scan vs V-scan suit different feature geometries. H-scan (leftmost / rightmost x per row y) works best for mostly-vertical boundaries like breast and tail sides. V-scan (topmost / bottommost y per column x) works best for mostly-horizontal boundaries like the back arch and belly bottom.
- RDP ε ≈ 12 at step 4 is a reasonable default for a 1920 px reference scaled to 500 px. Produces ~10–20 pts per region boundary — enough for smooth Catmull-Rom bezier without over-fitting noise.
- Scan output is a starting point, not a finished path. The automated boundaries need stitching, seam-point selection, and a Catmull-Rom pass before they become usable SVG
<path>data. Features with poor colour separation (orange tail ≈ orange body) or complex topology (legs inside body bbox) require manual override.
V2 Redraws v2-redraw
- feTurbulence is a performance trap. Applying multiple feTurbulence-based filters (especially nested inside clip-paths) can hang the browser renderer entirely — screenshot timeouts, frozen pages. For iridescent spots, hand-placed blurred ellipses/circles are faster and give more control over placement.
- Front-facing birds need a unified silhouette. Drawing head and body as separate shapes always produces a "ball on an egg" look. A single
<path>for the entire outline, with colour regions overlaid, produces a much more natural shape. The orange-to-brown transition zones (blended edge paths at ~40% opacity) hide the seam between colour regions. - Flat geometric style is the easiest to reproduce accurately. When the reference is already stylised (baseline.jpg), shape accuracy is high because the target shapes are simple primitives. Photorealistic birds (robin.jpg, hummingbird.jpg) are harder because organic shapes resist clean SVG paths.
- Layered tail feathers beat splayed fingers. Drawing separate tail feather paths that overlap slightly (each 8-10px apart vertically) with consistent curvature reads as a natural fan. Drawing them all from one origin point with different end angles looks like a hand.
- feGaussianBlur filter for spot softening is lightweight. A simple
<feGaussianBlur stdDeviation="1.5"/>on individual circles is cheap and renders instantly, unlike feTurbulence masks which compound exponentially.
Workflow
- Five render-feedback iterations. View → zoom into problem areas → identify specific failures → fix exactly those → repeat. Never speculate; see it first.
- Tracing beats freehand for accuracy. Match the SVG
viewBoxto the photo's pixel dimensions and use the reference as a semi-transparent overlay to calibrate before building the final file.