Back to writing

Browser in the Dark: flashlights with CSS and canvas

    Table of contents
  1. mix-blend-mode
  2. Canvas
  3. Night Light with CSS
  4. Night Light with canvas

Note: this article will make more sense if you are using a mouse or other pointing device. Requires javascript.

Warning: things can get a little spooky in the dark!

You can read the source code for this document here (or by pressing Ctrl+U in chrome / firefox).

mix-blend-mode (§)

I really like the "dark mode" switch on [1]. It's a fun subversion of expectations and reminds me of Night Light (1995), a PC game for kids about using a flashlight in the dark.

One of the splash texts I put on my homepage a while back says "looks better in black and white". Yesterday, while hypnotized by my own pulsating creation [2], I had the idea to put the two together and create a grayscale "flashlight", or perhaps a lens. I learned that the CSS property mix-blend-mode allows me to make a div that turns everything below it gray. The other mix-blend-mode options are cool too.

The results don't look exactly like a light source, but it's a nice toy. Here are some different flashlights and photos with which to admire them:

It applies to all elements on the page. sample sample sample sample sample.

♫ It's just your imagination
♫ night time fascination

Canvas (§)

I think those effects are fun, but they don't really give you the feeling of being in the dark like tonsky's flashlight switch. His works by setting the background color to black, so all the black text "disappears" even though it's still there. I wanted to come up with a flashlight that really works for all elements on the page, but I couldn't figure it out with CSS.

Instead, I was able to get a basic flashlight effect working with canvas by covering the entire screen in a black rectangle, and punching a hole in it.

If at any time you need to come back, all you must do is open your eyes.

This works great in conjunction with the mix-blend-mode lights:

Go back up and look at the images again!

After posting this article to Hacker News, I got some great suggestions for solving this problem with CSS:

    clip-path: circle(80px at var(--lightx) var(--lighty));


    position: fixed;
    z-index: 1;
    inset: -5px;
    background: radial-gradient(circle at var(--lightx) var(--lighty), transparent 0, transparent 100px, black 110px);
    pointer-events: none;

where lightx and lighty are set by a mousemove handler. I haven't assessed the performance impact of the Canvas solution versus these CSS solutions, but if I need to do this again in the future I think I will preferentially reach for the CSS version since it fits well with my usual way of doing things.

Night Light with CSS (§)

I also wanted to try making a Night Light game effect. The player who recorded the above video also uploaded the game .iso to the Internet Archive. Thank you Vulnerose!

I was able to open the iso in 7-zip, extract the level artwork as bmp files, produce the toy below. I've made a followup article with the other assets.

I first tried to approach it with mix-blend-mode, but I don't think you can use those effects to produce arbitrary image masks. I got it done with a more traditional method of translating a background-image property. The only jank fix I'm not really happy about is the background-size property for the flashlight layer, which I had to set using px values in javascript to match the rendered size of the parent. All the other background-size options created ill effects when I wanted to change the size of the flashlight to anything other than 100%. But I'm not that good with CSS and there might be a better answer.

Please turn off your flashlight first. Or don't, and enjoy the two effects together!

And, I couldn't help it:

Night Light with canvas (§)

The background-image version of Night Light works okay, but the revealed image is sometimes a few pixels out of alignment, which weakens the illusion. This depends on how much you've resized your browser and is especially noticeable with the vertical lines on the "close out sale" window.

This was a good opportunity for me to get practice with canvas, and I find the results look a lot smoother, so here's the same game again.

Have fun!

[1] Archived here in case it gets changed.

[2] A little vanity goes a long way!

View this document's history

Contact me:

If you would like to subscribe for more, add this to your RSS reader: