Nothing's lighting effect explained and tutorial

I've had a few people ask me about how I handle the lighting in my entry Nothing. I get asked wether it's a shader or some other coding magic. So I thought I'd take the time to explain it and procvide the code and explain it the best I can.
This lighting effect is inspired by the one used in Minit and I honestly wanted to just see if I could recreate it. From this is where my visuals in my own entry came from.

I'm using GameMaker studio 2 in this tutorial - so for any none gamemaker users I'm not sure how useful this will be for you but maybe you'll find it interesting regardless.
* Lets start things up by first creating a new object*
create event
//we're going to be using surfaces to recreate this effect
//step one create a surface the size of the current room and assign it a name
lighting = surface_create(room_width, room_height);
//next fill the surface with our specified color - in my game I use a slightly blueish black but this can be replaced with any color
surface_set_target(lighting);
sdraw_clear_alpha($21280D, 0);
surface_reset_target();
end step event
//normally most objects make use of a step event, but because we want to ensure the lighting effects are done last we use a step end event
if (surface_exists(lighting)) { //surfaces can be volatile, so you should always check if they exist before drawing to one
surface_set_target(lighting);
// The next few steps set the dark overlay
draw_set_color($21180D);
draw_set_alpha(1); // here is an optional line that reduces the darkness of the room
draw_rectangle(0, 0, room_width, room_height, 0);
// Setting the blend mode to 'subtract' is what allows us to "cut holes" out the surface we made
gpu_set_blendmode(bm_subtract);
draw_set_color(c_white); //anything drawn in white will now remove the overlay in that position
//minit lighting system
draw_circle(mouse_x, mouse_y, 100, false); // here is where the magic happens
// Reset all of your draw stuff back to normal again
gpu_set_blendmode(bm_normal);
draw_set_alpha(1);
surface_reset_target();
}
else
{
// sometimes surfaces can fall apart randomly so here we
// Create a new one if there were any issues
lighting = surface_create(room_width, room_height);
surface_set_target(lighting);
draw_clear_alpha(c_black, 0);
surface_reset_target();
}
draw event
// Draw the surface or create a new one if the old one borked out
if (!surface_exists(lighting))
{
lighting = surface_create(room_width, room_height);
}
else
{
if (view_current == 0)
{
gpu_set_colorwriteenable(true,true,true,false);
draw_surface(lighting, 0, 0);
gpu_set_colorwriteenable(true,true,true,true);
}
}
ok so what's this do?
Well if you were to take this code and run it right now you wouldn't get that sweet dithering effect you'd see in Minit and instead see this:

Currently its a simple circle - it's a neat effect and I even use it my game to create the pinhole transition used at the beginning, but thats not why we're here. To get the dithering effect we need to be drawing some sprites with the dithering built in. specificially something like these:
These are just 4 simple sprites with varying levels of dithering - with the first sprite being entirely white and the rest getting progressively more transparrent (note: to show this better the transparent pixels in the above are represented with black pixels)
so now we have the sprites, how do we use them?
simply replace the line: drawcircle(mousex, mouse_y, 100, false); // here is where the magic happens with a nested for loop: for(var my = -5; my <= 5; my++) //now the magic really happens { for(var mx = -5; mx <= 5; mx++) {
var dist = point_distance(mouse_x, mouse_y, mouse_x + mx*16, mouse_y+ my*16);
{
if(abs(dist) <= 2*16) draw_sprite(s_lighting, 0, (floor(mouse_x/16) + mx)*16, (floor((mouse_y-24)/16) + my)*16);
else if(abs(dist) <= 3*16) draw_sprite(s_lighting, 1, (floor(mouse_x/16) + mx)*16, (floor((mouse_y-24)/16) + my)*16);
else if(abs(dist) <= 4*16) draw_sprite(s_lighting, 2, (floor(mouse_x/16) + mx)*16, (floor((mouse_y-24)/16) + my)*16);
else if(abs(dist) <= 5*16) draw_sprite(s_lighting, 3, (floor(mouse_x/16) + mx)*16, (floor((mouse_y-24)/16) + my)*16);
else
{
}
}
}
mx = 0;
}
The aboe simply checks if the current position is x amount of tiles away from whatever origin point - in this case the mouse pointer. my tiles are 16 * 16 in size which is why everything is done with a check of 16.
now what we get is:

And there we have it a simple effect done simply.
You can add some other things too like having a sinWave effecting the distance to give off a pulsing / flickering effect.
Personally I find this effect only really looks good with a limited color pallet but feel free to try it out in whatever you're working on, try it out and send me some screenshots with with different colors and sprites and if you use it show me on twitter @JoelLikesPigs
Hope that explains that - hit me up with any questions if you're lost on anything.

