This was supposed to be a kind of tutorial, but I got distracted along the way and it devolved into a demo.

# Dwitter 3D "engine"

Or "How to write shaded and animated 3D scenes in <140 chars of javascript"

Dwitter gives us some freebies:
```  Our code is called 60 times per second.
t: Elapsed time in seconds.
S: Shorthand for Math.sin.
C: Shorthand for Math.cos.
T: Shorthand for Math.tan.
R: Function that generates rgba-strings, usage ex.: R(255, 255, 255, 0.5)
c: A 1920x1080 canvas.
x: A 2D context for that canvas.
```

Here's a circle drawn with 2000 fillRect calls:

```c.width|=0 for(i=2e3;i--;x.fillRect(960+S(i)*99,540+C(i)*99,9,9));```

Simple shading - color determined by the angle.

```c.width|=0 for(i=2e3;i--;x.fillRect(960+S(i)*99,540+C(i)*99,9,9))x.fillStyle=R(S(i)*255)```

Let's make it into a tube, instead of using 960 as the horizontal center we'll use i.

```c.width|=0 for(i=2e3;i--;x.fillRect(i+S(i)*99,540+C(i)*99,9,9))x.fillStyle=R(S(i)*255)```

Not very solid. We can fix that in a few ways. Best not to draw more than 2000 rects per frame for performance though.

So, either lower resolution, bigger rects or a shorter tube.

```c.width|=0 for(i=2e3;i--;x.fillRect(i/4+S(i)*99,540+C(i)*99,9,9))x.fillStyle=R(S(i)*255)```

Well, that's a start I guess. Let's center it.

```c.width|=0 for(i=2e3;i--;x.fillRect(700+i/4+S(i)*99,540+C(i)*99,9,9))x.fillStyle=R(S(i)*255)```

And use larger rects.

```c.width|=0 for(i=2e3;i--;x.fillRect(700+i/4+S(i)*99,540+C(i)*99,19,19))x.fillStyle=R(S(i)*255)```

Let's move it up and down...

```c.width|=0 for(i=2e3;i--;x.fillRect(700+i/4+S(i)*99,540+S(t)*400+C(i)*99,19,19))x.fillStyle=R(S(i)*255)```

Now - let's try attaching our up/down motion to a change in lighting. The idea is to make it look like tube is moving above and below the light source.

```c.width|=0 for(i=2e3;i--;x.fillRect(700+i/4+S(i)*99,540+S(t)*400+C(i)*99,19,19))x.fillStyle=R(S(i-S(t))*255)```

Yeah, ok, I can live with that. We can sort of fake specularity by raising our lighting coefficient S(i-S(t)) to an odd power.

```c.width|=0 for(i=2e3;i--;x.fillRect(700+i/4+S(i)*99,540+S(t)*400+C(i)*99,19,19))x.fillStyle=R(S(i-S(t))**5*255)```

Odd powers mean that negative numbers stay negative. Using even powers will get you highlights on the inside as well - which may or may not be a good thing.

```c.width|=0 for(i=2e3;i--;x.fillRect(700+i/4+S(i)*99,540+S(t)*400+C(i)*99,19,19))x.fillStyle=R(S(i-S(t))**6*255)```

896 is 1110000000 in binary, or 128*7, so i&896 gives us a change in hue every 128 iterations.

```c.width|=0 for(i=2e3;i--;x.fillRect(700+i/4+S(i)*99,540+S(t)*400+C(i)*99,19,19))x.fillStyle=`hsl(\${i&896},50%,\${S(i-S(t))**6*255}%)````

Too much contrast!

```c.width|=0 for(i=2e3;i--;x.fillRect(700+i/4+S(i)*99,540+S(t)*400+C(i)*99,19,19))x.fillStyle=`hsl(\${i&896},50%,\${(.5+S(i+t))**9+22}%)````

And let's make the whole thing rotate along with the light source.

```c.width|=0 for(i=2e3;i--;x.fillRect(700+i/4+S(i+t)*99,540+S(t)*400+C(i+t)*99,19,19))x.fillStyle=`hsl(\${i&896},50%,\${(.5+S(i+t))**9+22}%)````

Make it bigger by stretching the Y portion and updating the sizes in fillRect. Width will remain as is, so our circles will turn into ellipses, which will also help with the 3D feel.

```c.width|=0 for(i=2e3;i--;x.fillRect(700+i/4+S(i+t)*99,540+S(t)*400+C(i+t)*200,22,44))x.fillStyle=`hsl(\${i&896},50%,\${(.5+S(i+t))**9+22}%)````

Flip x and y:

```c.width|=0 for(i=2e3;i--;x.fillRect(700+S(t)*400+C(i+t)*200,540+i/4+S(i+t)*99,44,22))x.fillStyle=`hsl(\${i&896},50%,\${(.5+S(i+t))**9+22}%)````

Recenter and lose the motion

```c.width|=0 for(i=2e3;i--;x.fillRect(960+C(i+t)*200,340+i/4+S(i+t)*99,44,22))x.fillStyle=`hsl(\${i&896},50%,\${(.5+S(i+t))**9+22}%)````

Extract the radius and modulate it

```c.width|=0 for(i=2e3;i--;x.fillRect(960+C(i+t)*r*2,340+i/4+S(i+t)*r,44,22))r=C(i/770)*99,x.fillStyle=`hsl(\${i&896},50%,\${(.5+S(i+t))**9+22}%)````

This messed up the lighting. Since the radius sometimes goes negative, there's no highlight on the bottom now. Simple solution - square the radius so it's always positive.

```c.width|=0 for(i=2e3;i--;x.fillRect(960+C(i+t)*r*2,340+i/4+S(i+t)*r,44,22))r=C(i/770)**2*99,x.fillStyle=`hsl(\${i&896},50%,\${(.5+S(i+t))**9+22}%)````

Let's lose the colors, and just keep the lightness component. Maybe add a small constant to the radius.

```c.width|=0 for(i=2e3;i--;x.fillRect(960+C(i+t)*r*2,340+i/4+S(i+t)*r,33,18))r=9+C(i/770)**2*99,x.fillStyle=R(a=(.8+S(i+t))**9+22,a,a)```

Change lighting to light the inner part too.

```c.width|=0 for(i=2e3;i--;x.fillRect(960+C(i+t)*r*2,340+i/4+S(i+t)*r,33,18))r=9+C(i/770)**2*99,x.fillStyle=R(a=S(i+t)**8*200,a,a)```

And change the light direction.

```c.width|=0 for(i=2e3;i--;x.fillRect(960+C(i+t)*r*2,340+i/4+S(i+t)*r,33,18))r=9+C(i/770)**2*99,x.fillStyle=R(a=S(i+t+1)**8*200,a,a)```

Maybe paint the thing gold?

```c.width|=0 for(i=2e3;i--;x.fillRect(960+C(i+t)*r*2,340+i/4+S(i+t)*r,33,18))r=9+C(i/770)**2*99,x.fillStyle=R(a=S(i+t+1)**8*200+99,a*.7,a/4)```

Hey! We've got some sort of a golden chalice thing with nasty edges! Let's try lower res (-1 is invalid as a canvas width, so it gets set to the default, which is 300).

```c.width=-1 for(i=2e3;i--;x.fillRect(150+C(i+t)*r,40+i/22+S(i+t)*r/2,9,4))r=9+C(i/770)**2*44,x.fillStyle=R(a=S(i+t+1)**8*200+99,a*.7,a/4)```

Edges still pretty nasty but we shaved off a few chars. Shave a few more.

```c.width=-1 for(i=2e3;i--;x.fillRect(150+C(--m)*r,40+i/22+S(m)*r/2,9,4))r=9+C(i/770)**2*44,x.fillStyle=R(a=S(m=i+t+1)**8*200+99,a*.7,a/4)```

We can fix the top of the chalice by adding i/66 as the alpha in our rgba.

```c.width=-1 for(i=2e3;i--;x.fillRect(150+C(--m)*r,40+i/22+S(m)*r/2,9,4))r=9+C(i/770)**2*44,x.fillStyle=R(a=S(m=i+t+1)**8*200+99,a*.7,a/4,i/66)```

And the bottom by moving it offscreen.

```c.width=-1 for(i=2e3;i--;x.fillRect(150+C(--m)*r,70+i/22+S(m)*r/2,9,4))r=9+C(i/770)**2*44,x.fillStyle=R(a=S(m=i+t+1)**8*200+99,a*.7,a/4,i/66)```

Now, shave off a bit more and add brightness.

```c.width=-1 for(i=2e3;i--;x.fillRect(150+C(--m)*r,70+i/22+S(m)*r/2,9,4))r=9+C(i/770)**2*44,x.fillStyle=R(a=S(m=i+t)**8*255+128,a*.7,a/4,i/66)```

Scale up

```c.width=-1 for(i=2e3;i--;x.fillRect(150+C(--m)*r,30+i/15+S(m)*r/2,9,6))r=9+C(i/770)**2*44,x.fillStyle=R(a=S(m=i+t)**8*255+128,a*.7,a/4,i/66)```

TA-DA!

...ok, so it's not the prettiest thing in the world, but with a bit more effort this technique can also be used to make jack o'lanterns, fish, flowers, snails, and bananas(!)

Now go make some tiny 3D demos :)