Bug in a Rug tests M. C. Escher-inspired point-remapping on a 2D raster image: a drawn height field shifts source pixels to simulate depth, while proving that color changes are part of the illusion, not decoration.

Brush
Type
Toy note
Status
Active toy note
Published
2026-05-01
Canonical URL
https://shanecurry.com/lab/toys/bug-in-a-rug/
Related project
ChewGum Animation
Source image
Gum PNG drawn by Shane Curry

Dragging writes height into the source map. Each stroke starts at zero height, fades up to the active brush, and leaves a persistent raised trail. The toy then remaps source pixels and adds brush-side tint, shade, and rimlight cues from the same height data.

The effect is readable only when geometry and color agree. Black areas and flat-color areas can destroy the depth cue because there is no local texture for the remap to reveal; the brush needs tint, shade, and a restrained rimlight to help a flat raster image read as a raised surface.

This is not an Escher reconstruction, but it touches the same broad idea of a picture being governed by a coordinate map. Escher's Print Gallery has been studied as a mathematical image transformation, and his circle-limit work is often discussed as a distorted map of hyperbolic space. Bug in a Rug keeps the idea much smaller: source-image coordinates in one raster image are lifted by a brush-written height map, projected, tinted, shaded, and drawn back to the screen.

Source
Built-in Gum PNG at its native 460 by 460 pixel size
Purpose
Experiment with 2D raster point-remapping as a depth illusion, and test how much the illusion depends on source color plus brush tint, shade, and rim cues
Stroke model
Dragging waits for a committed direction, then max-writes brush falloff into a persistent height-map array; each new stroke eases from zero height to the active brush height
Brush shapes
Square and circle use different footprint helpers: square computes side-band falloff, while circle computes radial falloff. Both feed the same height, shadow, and rimlight rules.
Footprint size
Controls the plateau size of the active brush
Projection
Each committed stroke records one stroke-following projection direction; full-height projection is capped at 12 pixels
Coordinate map
screen = source + stroke_direction[source] * projection * height_map[source]; the overlay shows the projected mesh vertices used by the texture
Height highlight
Source-space height texture: each raised pixel fades from zero to grey; it does not turn the ramp white
Height shadow
Source-space shadow texture: each pixel checks the height drop along that pixel's stored stroke direction, so only one stroke-directed slope darkens
Rimlight
A separate subdued rim band appears only on top-face pixels at the edge opposite the shadow, with alpha scaled by projection height
Cell map
Optional; swaps the source texture from Gum to a source-coordinate checker map, then uses the same projected mesh
Color dependency
Solid black and broad flat colors make the remap harder to read; tint, shade, rimlight, and source texture are part of the perceptual proof
Ramp width
Controls the falloff width around the active brush; square keeps directional side bands, while circle uses radial falloff
Draw order
Base image first, then textured mesh triangles sorted from lower height to higher height
Surface model
Brush plateau plus falloff bands; no separate pasted side faces in normal mode
Debug geometry
Optional; shows the projected mesh, current brush footprint, projected plateau, and projection direction
Sampling
Nearest-neighbor sampling, so Gum and the checker map stay pixel-readable instead of being smoothed
Scope
This is a 2D raster point-remapping trick with tint and shade cues; it does not claim physical surface behavior
  • This toy follows earlier image-warp sketches but replaces the failed field warp and pasted-box attempts with a projected mesh.
  • The interaction is narrowed to a brush-written height map, per-stroke projection direction, brush falloff, and the Gum texture.
  • The current purpose is to test whether 2D raster point-remapping can simulate depth, and where the illusion fails when source color or brush tint/shade is removed.
  • The built-in image is Gum, supplied by Shane Curry for this demo.
  • Escher and the Droste effect documents the mathematical structure behind Escher's Print Gallery.
  • EscherMath on hyperbolic geometry describes the circle-limit prints as distorted maps of hyperbolic space.

Shane Curry, "Bug in a Rug," https://shanecurry.com/lab/toys/bug-in-a-rug/, published 2026-05-01.

Nothing is hiding under the picture except one raster image, a height-map array, a coordinate remap, and the color cues needed for that remap to read as depth.