Diffusion Curves for Vector Painting
Author colored curve constraints, rasterize them onto a bounded grid, solve a smooth field, inspect residuals, and archive a reproducible painted edition.
Diffusion curves turn editable colored paths into smooth-shaded images by propagating constraints through the surrounding field. This tutorial builds a bounded vector-painting study where geometry, color sides, solver iterations, residual error, and export provenance all remain visible.
Diffusion curves make boundaries into paint
Conventional vector art fills closed shapes or applies gradients with explicit handles. Diffusion curves describe color conditions along paths and let a solver propagate those colors through the image plane. A single curve can assign different colors to its two sides, creating soft material transitions without constructing many overlapping translucent shapes.
The Adobe Research publication and original paper describe the representation and reconstruction. This tutorial uses a small iterative grid to expose the idea, not to reproduce a production renderer or hierarchical solver.
Keep the authored object vector-like: cubic curve geometry, sampled parameters, left and right color stops, discontinuity flags, and layer order. The displayed painting may be rasterized, but its constraints remain editable and serializable.
That separation preserves authorship.
The bundled canvas uses generated boundary colors and fixed Jacobi iterations. It proves finite deterministic propagation on a bounded grid; it does not imply native SVG support for diffusion curves or publication-grade convergence.
- The centerline is editable vector geometry.
- Left and right color constraints may vary along the curve.
- The solver propagates constraints into surrounding image space.
| Signal | Interpretation |
|---|---|
| Two-sided color constraints on Bézier curves | Editable curves carry distinct left and right colors that seed a continuous painted field rather than outlining filled shapes. |
Author geometry before solving color
Begin with a few curves that describe the major illumination and material boundaries of the image. Place endpoints and tangent handles to carry visual flow, then inspect intersections, near-tangencies, tiny loops, and gaps. Solver sophistication cannot rescue a curve network whose geometry contradicts the intended volumes.
Sample each cubic at an adaptive or documented fixed rate. Closely curved regions need enough samples to seed the grid without gaps, while long straight runs can use fewer. Retain the original control points and sampling rule so a later export can be regenerated at another resolution.
The SVG path morphing guide provides related curve sampling and topology discipline. Diffusion curves add sided color constraints, so curve orientation matters: reversing a path can swap which color is considered left or right unless the representation normalizes it.
Render a diagnostic view containing centerlines, direction arrows, control handles, sample points, and color swatches. This wire view should remain available beside the painted result so editing never becomes blind pixel pushing.
Attach color constraints to both sides
Assign color as a function of position along the curve, independently for each side. Simple studies can interpolate a few stops; richer surfaces may use more stops or permit a color discontinuity. Store colors in an explicit space and note how they are converted for the solver and display.
Curve color constraints should reflect the image's material logic. A warm illuminated side and cool shadow side can imply a fold; similar values with a hue shift can make a subtler surface. Avoid adding many curves to chase every pixel, because the representation is strongest when a small set organizes a broad smooth field.
Check endpoint behavior. Colors from adjacent curves can conflict where paths meet or approach one another. Define whether endpoints merge, overlap by layer order, or leave a gap for unconstrained interpolation; make the choice visible in the editor.
The two-sided figure offsets color strokes around one centerline for explanation. A real rasterizer maps samples into grid cells and must resolve which nearby cells belong to each side without leaving pinholes.
Runnable artifact — The Canvas painter diffuses two generated boundary colors across a fixed grid and reports the exact Jacobi iteration count.
<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>Diffusion curve painter</title><style>body{font:16px system-ui;max-width:800px;margin:2rem auto;padding:1rem;background:#101827;color:#f5f0e8}canvas{width:100%;height:auto;border:1px solid #91a3bd}button{padding:.7rem;margin:.3rem}</style><h1>Diffusion curve painter</h1><p>A generated pair of color constraints diffuses across a bounded grid.</p><canvas id="field" width="640" height="360"></canvas><button id="iterate">Add 20 iterations</button><output id="receipt" aria-live="polite"></output><script>const c=field.getContext('2d'),w=64,h=36;let steps=0,grid=Array.from({length:w*h},(_,i)=>{const x=i%w;return x<3?[245,84,108]:x>w-4?[52,211,197]:[16,24,39]});function solve(n){for(let k=0;k<n;k++){const next=grid.map(v=>v.slice());for(let y=1;y<h-1;y++)for(let x=3;x<w-3;x++)for(let ch=0;ch<3;ch++)next[y*w+x][ch]=(grid[y*w+x-1][ch]+grid[y*w+x+1][ch]+grid[(y-1)*w+x][ch]+grid[(y+1)*w+x][ch])/4;grid=next;steps++}const img=c.createImageData(w,h);grid.forEach((v,i)=>{img.data.set([...v.map(Math.round),255],i*4)});const tmp=document.createElement('canvas');tmp.width=w;tmp.height=h;tmp.getContext('2d').putImageData(img,0,0);c.imageSmoothingEnabled=true;c.drawImage(tmp,0,0,640,360);receipt.value='PASS: bounded diffusion field reached '+steps+' Jacobi iterations'}iterate.onclick=()=>solve(20);solve(80)</script></html>
| Residual state | Reading |
|---|---|
| High at curve | constraint rasterization or discontinuity issue |
| High in field | more iterations or better solver needed |
| Low but banded | grid resolution may still be visible |
| Signal | Interpretation |
|---|---|
| Poisson residual heat field | A gridded residual map concentrates error near fresh constraints and fades as iterative relaxation converges. |
Rasterize constraints onto a bounded grid
Choose a solving grid that is large enough to preserve important curve separation but small enough for interactive iteration. Map curve samples into cells, mark fixed values, and initialize unconstrained cells from a neutral field or coarse approximation. Record the transform from vector coordinates to grid coordinates exactly.
At each sample, estimate the tangent and a perpendicular direction to seed left and right constraints. Handle tight curvature and intersections carefully because a one-cell offset can cross another boundary. Visualize the seed cells before solving; most mysterious color leaks begin in constraint rasterization rather than the linear system.
Padding and boundary conditions affect the frame. Decide whether outer edges hold a background color, copy nearby values, or solve under another condition. The choice shapes broad gradients and belongs in the edition receipt.
Diffusion curves can be evaluated on multiresolution or specialized solvers, as the hierarchical follow-up explores. The teaching grid stays deliberately small so every seeded value and iteration is inspectable.
Solve the smooth field and watch residual
For unconstrained cells, iterative relaxation replaces a color with the average of neighbors while fixed constraint cells remain unchanged. Repeating that update approximates a harmonic field between boundaries. Apply the procedure per channel in a suitable numeric space, using double buffering so an iteration does not mix old and new updates accidentally.
Compute a residual: the difference between a cell and the neighbor average it should satisfy. Track maximum and aggregate residual away from constraints. Stop at a documented tolerance or iteration cap, and label a cap-limited result rather than calling it converged automatically.
Jacobi iteration is easy to teach and parallelize but can converge slowly. A stronger implementation may use Gauss-Seidel variants, multigrid, or sparse solvers. Preserve the same constraint corpus so solver changes can be compared through residual and image diagnostics.
The Poisson image interpolation heat field shows where error remains. It is a numerical map, not an aesthetic score; a low residual can still produce uninteresting or poorly art-directed color.
Art-direct smooth-shaded vector art
Once the field is stable enough, edit curves rather than painting over the raster. Move a boundary to change the perceived volume, adjust one side's stops to tune bounce light, or add a curve where a true discontinuity is missing. Compare the wire view and painting continuously.
Use a limited palette and intentional focal hierarchy. Broad low-contrast transitions can create atmosphere, while a few sharper sided constraints establish edges. The spectral color mixing article offers another color model when the artistic thesis depends on material-like mixing rather than screen-space interpolation.
Inspect banding, haloing, constraint leakage, endpoint artifacts, and grid imprint at target size and zoom. Increase resolution or improve rasterization only after identifying which stage owns the defect. A blur applied at the end may hide the evidence while weakening deliberate boundaries.
Diffusion curves are a representation for smooth-shaded vector art, not a promise that every painted effect compresses elegantly into paths. Keep raster textures separate when they serve a different visual language.
Export geometry, raster, and evidence
Save the curve network as structured data with namespaced identifiers, cubic controls, orientation, side colors, stop parameters, layer order, coordinate transform, color space, solver version, grid, iteration count, tolerance, and residual summary. Export a raster preview in an optimized format for reliable display.
An SVG can embed the authored curves, diagnostic overlays, and perhaps the raster result, but standard SVG does not natively reconstruct the diffusion field described here. Label the asset honestly instead of presenting a large sampled mosaic as a browser diffusion-curve primitive. Keep both exports.
The Coons patch mesh-gradient guide offers a geometry-driven alternative that samples a four-sided parametric surface. The two methods produce different editing affordances: boundary-color fields versus a controlled surface lattice.
Include a text legend describing curve roles and colors so the visual remains understandable without color alone. Archive one deterministic thumbnail and its receipt for future renderer comparisons. Keep the receipt beside every export.
- Curve geometry and colors stay constant across the strip.
- Only the deterministic iteration count changes.
- The final export stores grid, solver, tolerance, and residual beside the artwork.
| Signal | Interpretation |
|---|---|
| Iterations-to-painted-field convergence strip | Four panels progress from sparse curve colors through broad diffusion to a smooth bounded field while geometry remains fixed. |
Build a painter that refuses hidden error
Test isolated curves, closed loops, intersections, close parallels, reversed orientation, shared endpoints, extreme colors, tiny grids, iteration caps, and empty constraints. Reject non-finite cells, expose residual above tolerance, and retain the last valid result when an edit produces an invalid state.
Performance work should preserve diagnostics. Move iteration to a worker or GPU only after the scalar fixture agrees on seeded cells and residual within a declared tolerance. The Canvas dithering guide can help when the final palette is intentionally limited, but apply it after field evaluation rather than confusing diffusion error with quantization texture.
Diffusion curves become compelling when the solver disappears from the viewer's experience but remains visible to the maker. The artistic mind shapes a handful of boundaries; the technical system proves how those constraints became pixels.
Open the painter, add twenty iterations, and inspect how the field changes. Then replace the straight generated boundaries with one authored cubic pair and archive both the wire geometry and residual before calling the edition finished.
Use vector shading for the authored intent, Poisson image interpolation for the bounded numerical problem, smooth-shaded vector art for the exported visual category, and curve color constraints for the editable source. The synthetic convergence strip should include the same curve digest at every stage so a renderer change cannot quietly move geometry while claiming only faster convergence. Diffusion curves remain editable precisely because that source record survives every raster preview.