Any part of the plane not covered by lights has an alpha of zero. If your lights fade out from a center like a soft spotlight, the lighting plane will end up with alpha values between 0 and the max (which I'll call 1 rather than 255, for simplicity).
The problem here is that you're likely using a color matrix for your lighting plane, and color matrices work differently depending on whether you're drawing an icon directly or whether it's being used for a plane or a KEEP_TOGETHER group. The plane has already been drawn on, so all of the color values conform to what you'd call premultiplied alpha. That is, where that red light you used is at half strength, it shows up on the plane as rgba(0.5,0,0,0.5) instead of rgba(1,0,0,0.5); the red, green, and blue channels have alreay taken alpha into account.
Color matrices work differently with premultiplied alpha; they'll first change the color back to its original non-premultiplied form, then do the matrix, then multiply back by the new alpha value. So if you have a matrix designed to turn the alpha all the way up to 1, those half-red pixels turn to rgba(1,0,0,1) which is full red, and your spotlight no longer fades out gracefully like you wanted.
A backdrop solves all of that.
obj/lighting_plane
plane = 2 // or whatever plane you use for lighting
screen_loc = "CENTER"
blend_mode = BLEND_MULTIPLY
// this has an ambient light of 20% gray added to all lights
color = list(null, null, null, "#0000", "#333f")
New(client/C)
C.screen += src
var/obj/O = new
O.blend_mode = BLEND_OVERLAY // this is important so it doesn't inherit
O.icon = 'blackness.dmi' // a black icon using your regular world.icon_size
O.layer = BACKGROUND_LAYER
overlays += O
UpdateView(C)
// call this any time client.view changes!
proc/UpdateView(client/C)
var/vx,vy
var/obj/O = new
var/list/L = overlays.Copy()
O.appearance = L[1]
if(isnum(C.view)) vx = vy = (2*C.view) + 2
else
L = splittext(C.view, "x")
vx = L[1] + 1
vy = L[2] + 1
O.transform = matrix(vx, 0, 0, 0, vy, 0)
overlays = list(O)
With the backdrop in place, you won't have any oddities where the lights fade out, because all of the lights (which presumably have BLEND_ADD) are drawn over an opaque black surface. The lighting plane is then totally opaque when you multiply it over your scene, which is good because you don't really want it changing the scene's alpha values anyway.
Savvy users may wonder: Why not use a screen_loc of "SOUTHWEST to NORTHEAST" instead? The reason is that this is represented as multiple tiles, which all have to be turned from appearances to icons (currently screen objects don't cache repeated tiles, which is less than ideal), put through the sorting process, and drawn, whereas by using transform you can have only a single very large icon. The engine likes that a heck of a lot better.