Here's an old technical breakdown of how I redesigned Celeste's lighting in 2017, before it came out: https://noelberry.ca/posts/celeste_lighting/
Remaking Celeste's Lighting

A technical breakdown on the lighting in Celeste

@noelfb Interesting that you are batching the shadow masks. I suppose because the mesh itself is very limited in size? I use geometry projection + destination alpha to store the masks so I have to draw each in a separate pass, though I can reuse the same (trivial) geometry batch for all lights.

I have a 3 part blog series about my methods here with more details:
https://www.slembcke.net/blog/SuperFastHardShadows/

Still need to finish the 4th part about fourier lightfields for normal mapped lighting though...

2D Lighting with Hard Shadows

Techniques to implement simple yet efficient hard shadows for a 2D game.

slembcke.net

@slembcke Nice, this is awesome - very thorough write up!

Yeah, I wanted to batch them because I was nervous about adding lots of draw calls per light on lower end platforms (circa 2017). It likely would be fine but I figured I'd merge them all together if I could.

@noelfb Most of those hard shadow tricks in article 2 I used in the mid to late 2000's. 2 draw calls per light isn't too bad when you realistically can't have more than a dozen or so on the screen at the same time and still make sense of them.

My soft shadow method from the 3rd article was designed for mid 2010's mobile hardware, and it flies! @vexpanse uses it along with lightfields (outputs to 5 RTs) and still runs fine on a Pi 4. :)

@noelfb I also put lights on _everything_. I think a typical scene has ~150 visible lights. (though most are small and don't cast shadows) Still runs absolutely great on something dinky like the Pi. :D https://www.youtube.com/watch?v=_rMzqY-RXh0
Veridian Expanse Gameplay Teaser

YouTube