4 KiB
| title | description | date | projectPeriod | thumbnail | tags | featuredOrder | role | stack | scale | outcome | audience | links | media | |||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Tile-Based Optimization for 2D SDF Ray Tracing | How SDF-2D used signed distance fields, dynamic shaders, and tile-based rendering ideas to make 2D ray tracing run well in the browser. | 2026-05-08 | Autumn-Winter 2020 |
|
|
3 | Library author |
|
Browser library with mobile-oriented real-time rendering and reusable demos | Reusable NPM package and thesis project for efficient 2D SDF rendering | recruiter-relevant |
|
|
SDF-2D was my attempt to make a small, reusable browser library for 2D scenes rendered with ray-tracing techniques. The rendering is based on signed distance fields, where geometry can be represented as functions that return the distance to the nearest surface.
The interesting part was not the basic idea. Signed distance fields are a known technique. The interesting part was making the approach fast and reusable enough for browser demos, including on mobile devices.
The project became one half of my BSc thesis, together with the multiplayer game decla.red, which used the rendering library in a real interactive setting.
The Problem
Ray tracing and distance-field rendering can produce appealing 2D lighting and reflections, but a straightforward implementation spends too much work per pixel. A browser library also has to deal with device variation: WebGL capabilities, shader limits, mobile GPUs, and the overhead of generating scenes dynamically.
The goal was not to render one hand-tuned scene. The goal was a library with a simple API, reusable scene definitions, and real-time behavior.
Constraints
The library had to support both WebGL and WebGL2. It had to run acceptably on phones. It had to avoid shipping scene-specific shader code by hand. And it had to expose an API that felt like a rendering library rather than a shader experiment.
Those constraints pushed the implementation toward generated shaders and capability-aware rendering paths.
Design
The main optimization was inspired by tiled renderers. Instead of treating the entire screen uniformly, the renderer could reason about groups of pixels and avoid unnecessary work where possible.
That was paired with deferred shading and dynamic shader generation. Dynamic generation mattered because scenes and devices differ. If a feature or operation was not needed for a given scene or device, the generated shader could avoid carrying that cost.
The API was deliberately kept in TypeScript. That made the library easier to package, document, and reuse in projects that were already browser-first.
What Worked
The project worked best when the library boundary was respected. A good demo can hide a messy implementation. A reusable package cannot. The API had to explain the rendering model without making every user think like a shader compiler.
The mobile constraint also improved the design. It forced performance work to be structural rather than cosmetic. When a technique works only on a powerful desktop GPU, it is easy to mistake headroom for good architecture.
What I Would Change
Today I would write more instrumentation around shader variants and device behavior. The project had many optimizations, but stronger profiling output would have made tradeoffs easier to explain and compare.
I would also document the rendering pipeline with diagrams. The ideas are visual, and the explanation should be too.