schmelczer-dev/src/content/posts/sdf-2d-ray-tracing.md

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
src alt
./_assets/sdf2d.jpg SDF-2D browser demo with soft lighting effects.
graphics
web
systems
3 Library author
TypeScript
WebGL
WebGL2
Signed distance fields
Browser library with mobile-oriented real-time rendering and reusable demos Reusable NPM package and thesis project for efficient 2D SDF rendering recruiter-relevant
label url
NPM package https://www.npmjs.com/package/sdf-2d
label url
Video https://www.youtube.com/watch?v=K3cEtnZUNR0
label url download
BSc thesis /media/downloads/sdf2d-andras-schmelczer.pdf true
type src alt caption
image ./_assets/sdf2d.jpg Browser demo page showing SDF-2D scenes rendered with soft lighting effects. SDF-2D was built as a reusable TypeScript library rather than a single demo.

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.