256 bytes JavaScript signed distance field raymarcher using 2D Canvas. **TEA STORM** won at Function 2013

You've seen distance field raymarching here before with JSpongy, Hypersonic Mandelbulb and ANDES to name a few. This technique championned by Iñigo Quilez of Elevated and Brave fame is perfect to render complex shapes in a few kilo bytes or even hundred bytes, but only a handful of size optimizers extraordinaire did raymarching in 256 or even 128 bytes.

## Source code

`<body onload=setInterval("for(t-=.1,x=h,c.height=300,Q=Math.cos;x--;)for(y=h;y--;c.getContext('2d').fillRect(x*4,y*4,N,N))for(N=D=4;X=D*x/h-D/2,Y=D*y/h-D/2,Z=D/2-9,D+=d=(X*X+Y*Y*Q(t/6+Q(D-X-Y))+Z*Z)/9-1+Q(X+t)*Q(Y-t),.1<d*N;)N-=.1",t=h=75)><canvas id=c>`

And voilà, 253 bytes of raymarching goodness.

## How does it work ?

This time, this is the real deal: No fixed step raymarching but a distance function that gives an estimate of how far is the surface of the object at each point in 3D space and the rays march on until they get close enough.

### The setup and loop

That part is trivial: A `canvas`

, a `body`

element with `onload`

event setting a timer that clears the canvas, adjust the time variable, go through each pixel and render them. Just make sure to reuse variables where possible, set the right properties and create alias variables where necessary.

`<body onload=setInterval("for(t-=.1,x=h,c.height=300,Q=Math.cos;x--;)for(y=h;y--;/* draw */)/* compute */",t=h=75)><canvas id=c>`

Once more with tons of comments:

`<body onload=setInterval("/* X loop from 75 to 0 */for(/* adjust the time variable */t-=.1,x=h,/* clear the canvas and size it to 300x300 i.e. 75*4, remember that the default resolution of a Canvas is 300x150 */c.height=300,/* alias for Math.cos */Q=Math.cos;x--;)/* Y loop from 75 to 0 */for(y=h;y--;/* draw the pixel(x,y) */)/* compute the intensity of the pixel(x, y) */",t=h=75)><canvas id=c>`

### About the resolution

MINI DISTRICT is actually a 2.5D effect; casts 150 rays checking at most 150 position along the rays which amount to 150x150 = 22.500 tests.

TEA STORM on the other hand is purely a 3D effect; casts 75x75 rays with up to 40 checks along the rays which amounts to a maximum of 75x75x40 = 225,000 tests.

That is a 10x more calculations. But the good news is that the distance function helps a lot here and things are not as dire as they seemed.

The 75x75 rays are blown up 4 times to fill up a 300x300 pixels canvas.

### The shades of grey

The default `fillStyle`

of Canvas is black and there is simply no bytes to waste on changing that. Instead the shades of grey are done by drawing axis aligned square with fractional size. This introduces anti-aliasing which results in various shades of grey instead of plain black and white. Simple.

```
c.getContext('2d').fillRect(x*4,y*4,N,N)
// with N in the range [0; 4]
```

### The camera

This time around, the camera is static. The object evolves over time because we tweak the distance function. The origin of the camera lies in {0, 0, -9} and looks toward {0, 0, 0} like this:

```
for(x=h;x--;)
for(y=h;y--;/* draw the shade of grey */)
for(N=D=4;
X=D*x/h-D/2,
Y=D*y/h-D/2,
Z=D/2-9,
/* here be dragons and distance function */
```

The variable `D`

represents how far we marched along the current camera ray.

### The distance function

What you see in TEA STORM is basically a sphere. A crazy twisted sphere, and here is the breakdown of the distance function and how this little monster came to life:

```
// Sphere
d=(X*X+Y*Y+Z*Z)/9-1
// Sphere morphing into a cylinder
d=(X*X+Y*Y*Q(t/6)+Z*Z)/9-1
// Bumpy sphere
d=(X*X+Y*Y+Z*Z)/9-1+Q(X+t)*Q(Y-t)
// Bumpy sphere-cylinder
d=(X*X+Y*Y*Q(t/6)+Z*Z)/9-1+Q(X+t)*Q(Y-t)
// Bumpy twirling morphing sphere-cylinder \(';;')/
d=(X*X+Y*Y*Q(t/6+Q(D-X-Y))+Z*Z)/9-1+Q(X+t)*Q(Y-t)
```

The loop for each ray look like this:

```
for(N=D=4;X=D*x/h-D/2,Y=D*y/h-D/2,Z=D/2-9,D+=d=(X*X+Y*Y*Q(t/6+Q(D-X-Y))+Z*Z)/9-1+Q(X+t)*Q(Y-t),.1<d*N;)
N-=.1
```

As you can see, `N`

, the number of iterations, and `D`

the distance, start at 4. This is a safe distance considering the origin of the camera. At each step, the new position along the camera ray is computed in 3D space in `X`

, `Y`

and `Z`

, the distance to the object is estimated in `d`

and added to `D`

, and `N`

decreased by 0.1.

The loop stops when the condition `.1<d*N`

is true. This condition does two things:

- Ensure that we stop when
`N`

reaches 0 or we have reached the object. - The multiplication
`d*N`

introduces an approximation of the focal distance.

This means that the further we are from the origin of the camera, the bigger the margin of of error we can allow without any visual artefact. Of course this is not entirelly correct since `N`

does not represent the distance travelled but the number of iterations, but this is good enough.

## Karma

TEA STORM won at the 256 bytes intro competition at Function 2013 in Budapest, Hungary on September 14th, 2013. As usual for my demoscene productions, TEA STORM is available on Pouet.net where any comments and gestures are appreciated.

Hope you like the little storm and write up.

## Other recent experiments

There are many experiments and projects like **TEA STORM** to discover other here.

**OUTER_M2**OUTER_M2: EXPLODING NEUTRINOS, a smashing JavaScript demo for the 1024 bytes demo competition at the Assembly 2019.**WE DON'T MAKE MISTAKES AT DEVONE 2019**Giving the closing keynote DEV ONE 2019, held on April 11 in Linz, Austria was absolutely fantastic. The conference was very well organized with one track, 11 talks about "scale" and 600 wonderful delegates. I learnt so much that day, got confirmation for some good practices but also learnt about many opportunities to improve our work. Also it was a pleasure to meet new people and see familiar faces among the organizers and attendees from Script'17.**MUSIC FOR TINY AIRPORTS AT WEB AUDIO CONFERENCE**The Web Audio Conference 2018, held in September 19-21 in Berlin was a great mix of researchers, web developers, artists and performers presenting their projects. I had the chance to provide a deep dive into music for tiny airports, explaining how to generate hours and hours of music in a handful of bytes.**DRAGON PUNCH**An itsy bitsy Dragon curve renderer in 121 bytes.**JAVASCRIPT IS JARIG**Javascript is 18 years old! Let's celebrate with a nice little tune.**MINI DISTRICT**How to build a 3D City in 256 bytes with Canvas 2D**TILT SHIFT**A very fast & simple tilt-shift effect in Canvas.**3D TOMB II**3D TOMB II: The tomb of the 4096 mummies is a first person shooter in a fully textured environment done in less than 4KB of JavaScript.**OOMA**The winning bootsector of Outline 2005, featuring two images zooming with experimental music in a valid 480bytes Atari bootsector.

## Let's talk

Don't be shy; get in touch by mail, twitter, github, linkedin or pouet if you have any questions, feedback, speaking, workshop or performance opportunity.