Opening the page…

Sulayman Bowles

Experiments

Tools and studies in light, texture, and motion.

The construction and rendering of this site’s sculptures.

I built the work objects as GLB assets, developed their ink-and-paper shading, and connected their moving parts to a renderer that suspends work when it is out of view.

Modeling a surface that survives halftone

The sculptures are built offline from modeled relief, cut openings, rounded shoulders, bearings, and separate moving parts. Bevels give lighting a wide enough transition to survive a binary threshold; recessed channels keep details readable at phone scale.

The forms include an engraved calendar and an escapement, portrait busts arranged around prototypes, a relief globe within armillary rails, and five meshing gears under open shrouds. Geometry is merged by material and part, centered, and exported with named pivots. The browser loads these files rather than rebuilding them every frame.

From lit surfaces to ink coverage

The portfolio shader converts the lit model to luminance, applies an ordered Bayer threshold, and maps coverage to ink and paper. Linear-light weights and display conversion keep material tone consistent; filtered transparent edges are handled before thresholding to avoid a dark fringe.

The dot pattern stays in a fixed grid. Subtle modulation changes the threshold continuously instead of assigning new random grain every frame. On Work, CSS-sized cells travel with the object’s viewport region, keeping grain and sculpture aligned while the page scrolls.

Motion from shafts and tooth counts

Moving nodes retain names, pivots, unit axes, and shaft metadata in their GLB extras. The five-gear mechanism uses a shared module and twenty-degree pressure angle. Its 36-tooth driver turns at 0.46 radians per second; neighboring ratios follow the tooth counts and reverse across each mesh.

The renderer advances shafts from accumulated visible playback time. Pause, a hidden tab, or an offscreen object stops that clock, preserving orientation on return. Direct interaction gets an immediate frame; automatic movement targets 30 fps, with settled paths for reduced motion.

Motion data retained with the object
Exported dataRuntime use
Local pivot and axisRotate the part around its modeled shaft, preserving the assembly.
Signed drive ratioAngle = visible time × driver speed × ratio.
Interactive name and extrasKeep selected parts and animation behavior intact through compression.

Smaller derivatives, retained geometry

The helmet and Contact derivatives reduce texture dimensions while retaining their compressed geometry bytes. The globe uses its existing compressed variant. Work and About models use Draco attribute quantization at 18/14/16 bits for positions, normals, and UVs, without reducing triangle counts.

The optimizer records hashes, byte sizes, hierarchy, materials, texture dimensions, and validator results. It checks names and animation extras before and after decoding. Loading uses one local Draco worker per renderer, abortable fetches, and disposal of results that arrive after their view has been replaced.

Portfolio derivatives · decimal MB
AssetSource → portfolioTriangles retained
Helmet2.26 → 1.76 MB299,992
Contact sculpture6.55 → 2.61 MB119,244
Globe1.64 → 0.41 MB78,130
Gear mechanism2.95 → 0.54 MB99,490

Sizes are from the asset manifest. Draco quantizes attributes; unchanged triangle counts do not mean bit-identical geometry.

Rendering work only when it is needed

Work caps its backing buffer at 1.35 million pixels and a DPR of 1.25. The runtime watches rendered-frame cadence separately from CPU submission time. After two two-second windows averaging over 40 ms, automatic motion reduces backing dimensions to 80%; eight seconds below 35 ms restores them.

Resolution changes wait during dragging and route handoffs. Offscreen suspension removes recurring frame work, while direct input can request a fresh frame. Development telemetry updates at most once per 750 ms so observing the renderer does not become another animation loop.