Make the Texture a Function
A 2D SDF evaluates each texel once and returns distance to a procedural boundary. Thresholds and smooth bands turn that value into bark, foliage, ground, shadows, and silhouettes without pixel-art assets.
Point-cloud speckles create foliage at distance. Shaded or solid triangles form trunks, branches, and ferns, while a UV-SDF deer sprite carries a recognizable figure through the same mathematical material system.
Animate a Complete World in Plain Python
The scanline architecture keeps every decision inspectable: project the geometry, evaluate material functions, shade spans, compose depth, and emit each frame.
Graphics programmers, educators, and artists can run the engine, alter the SDFs and palette, and use this compact world as a starting point for new procedural scenes.
Inspect The Render
Read The Implementation At One Glance
640x480 rendered frames.numpy and pillow.Follow One Scanline Through The Machine
The program projects scene geometry, generates its palette, evaluates procedural textures and SDF primitives, constructs the scene, and advances a camera path. Each scanline carries a filled mask: closer triangles and the deer occupy their pixels first, then background planes and ground material fill only uncovered positions.
The deer comes from rounded boxes and capsules in UV space. The billboard maps each candidate pixel back to u, v; deer_sdf(u, v) selects the silhouette; then albedo and shading functions color the occupied pixels.
The ground follows the same analytic path. A homography maps each screen row into ground UV coordinates, procedural fields create trail, duff, line, and fleck components, and the material function blends them without a stored source bitmap.
See The Two Decisive Row Functions
The plane function maps uncovered pixels into UV space and calls a material function. The deer function uses the same projection path, tests an analytic silhouette, then shades only the pixels inside it.
# Source excerpt from sdf_texture_scanline_renderer.pdf
# Dependencies: numpy, pillow
def draw_plane_row_sdf(img, filled, y, invH, edges, material_fn, palette):
fy = y + 0.5
inside = np.ones(W, dtype=bool)
for (A,B,C) in edges:
inside &= (A*fx + B*fy + C) >= 0.0
m = (~filled) & inside
if not m.any(): return
a_u, b_u, c_u = invH[0,0], invH[0,1], invH[0,2]
a_v, b_v, c_v = invH[1,0], invH[1,1], invH[1,2]
a_w, b_w, c_w = invH[2,0], invH[2,1], invH[2,2]
ru = a_u*fx[m] + b_u*fy + c_u
rv = a_v*fx[m] + b_v*fy + c_v
rw = a_w*fx[m] + b_w*fy + c_w
invrw = inv_nr3(rw)
u = ru * invrw; v = rv * invrw
col = material_fn(u, v, PALETTE)
idx = np.where(m)[0]
img[y, idx, :] = col
filled[m] = True
def draw_sprite_row_deer(img, filled, y, invH, edges):
fy = y + 0.5
inside = np.ones(W, dtype=bool)
for (A,B,C) in edges:
inside &= (A*fx + B*fy + C) >= 0.0
m = (~filled) & inside
if not m.any(): return
a_u, b_u, c_u = invH[0,0], invH[0,1], invH[0,2]
a_v, b_v, c_v = invH[1,0], invH[1,1], invH[1,2]
a_w, b_w, c_w = invH[2,0], invH[2,1], invH[2,2]
ru = a_u*fx[m] + b_u*fy + c_u
rv = a_v*fx[m] + b_v*fy + c_v
rw = a_w*fx[m] + b_w*fy + c_w
invrw = inv_nr3(rw)
u = ru * invrw; v = rv * invrw
d = deer_sdf(u, v)
inside_sprite = d <= 0.0
if not np.any(inside_sprite): return
idx_all = np.where(m)[0]
idx = idx_all[inside_sprite]
if idx.size == 0: return
uu = u[inside_sprite]
vv = v[inside_sprite]
albedo = deer_albedo(uu, vv)
shade = deer_shade(uu, vv)
col = np.clip(albedo * shade, 0.0, 1.0)
img[y, idx, :] = col
filled[idx] = True
Carry The Reusable Parts Into New Renderers
The UV-SDF material path, analytic deer sprite, and scanline filled-mask discipline form three separable techniques. Port them into a browser or GPU renderer, replace the scene and palette, and keep the same direct path from mathematical surface to visible material.

Comments added by Brian Greenforest on LinkedIn
This comment was also preserved verbatim from Brian Greenforest’s LinkedIn data export or the public post page.
Comment 1 · (2025-08-19 02:23:24 UTC)
View the LinkedIn post