Playchilla logo

Dynamic hard shadows in 2d

Before I forget, here is how I’ve done the shadows for the new version of Shadowess.


I realize that this blog post is not very detailed but rather should give you an idea of how shadow geometries can be generated. I may do a follow up with code in the future.

My goal was to create faster 2d shadows than I had previously done. To do this I use the GPU for the rendering.


First I naturally tried to use Away3d built in shadow casting system. However after adding only a few light sources I got an error message “register overflow”. After reading up on the away3D forums I came to the conclusion that it’s not built for many dynamic lights. Also it’s 3d shadows and 2d shadows would suffice for me.

Secondly I tried to use shaders as described here. This is probably performance effective, as calculations are done on the GPU. However, after fiddling around for an evening or two I felt that my shader skills weren’t sufficient and I didn’t really have time to learn. So I moved on…

Finally I decided to generate light geometries (disc) that had the shadows carved out. This seemed like a good solution for me, since I knew I could to it technically and it was just a matter of writing the code. I was a bit worried that it would be slow to upload new geometries all the time, but it turned out that it was not any problem.

Generating light geometries

The algorithm to generate the light geometries is simple and straight forward. However, to be honest, I had to spend a lot of time to patch up some corner cases. E.g. when triangulating around the 360 degrees I needed to make sure that the first point and last point fit. In fact all problems I had was due to fiddling in polar coordinates (angles). If I was to rewrite it, I would try to stay in vector space.

Here is a broad outline of the algorithm:

Imagine a light source with some scattered boxes around it as seen below. Each wall has two corner points, that acts as “outposts” for the shadow casting of the geometry. From now I will call those extreme points of the wall. This is demonstrated here, the white line in the wall acts as shadow caster line stretching between the two extreme points:

Get Adobe Flash player

When we have calculated those points, we shoot rays through them to see if the rays hit a wall near wall  (the point is occluded by a wall) or a far wall (projects shadow on another wall) or not at all (shadows ends at light radius). We store this information per point in a list.

Now we sort the new list with points clockwise or anti clockwise w.r.t. the light source. Now we just go through the list and draw triangles, some will follow walls, other will extend all the way to the light endpoint.

This is demonstrated here:

Get Adobe Flash player


And here with more walls:

Get Adobe Flash player

Now we just need to apply a light material to the geometry and we are done, also set blend mode to add!

Get Adobe Flash player



And with 30 walls and 20 lights (many optimization can still be done):

Get Adobe Flash player


    And what about projecting the shadow on a circle object?

    • You could create line segment spanning across a circle through its center, perpendicular to the light source and use the same algorithm.

      Maybe something like this (left, right become the endpoints of the line segment):
      const disc:Circle2 = wall.getBody() as Circle2;
      const discDir:Vec2 = disc.getPos().sub(lightPos);
      const d:Number = discDir.length();
      const f:Number = Math.sqrt(d * d – disc.getRadius() * disc.getRadius());
      const cosAlpha:Number = f / discDir.length();
      discDir.scaleSelf(1 / d);
      const rads:Number = Math.acos(cosAlpha);


      • I came across the solution, in reality we just need to use the Thales Theorem ('_theorem) to get the tangents of the circle that cross the light source. Thanks. =)

        • Yeah you are right, I was a bit too quick on my comment, the line isn’t supposed to go through the center but rather at the tangents. I think I tried to achieve that in the snipped above. However cool to see that it has a name! Thanks for letting me know.