Screenshot-proof images via temporal dithering #

Snapchat's (and now Facebook Poke's) main claim to fame is that it lets you send "self-destructing" image messages. Setting aside the debate about the uses of this beyond sexting, the key vulnerability in both apps is the built-in ability to take screenshots. Both take a reactive approach, where you're notified if the recipient took a screenshot, but can't really do anything about it.

I was thinking about ways of mitigating this issue, and figured that perhaps turning the image into an animation where individual frames are not (or at least less) recognizable would be the right path. This is a variant of temporal dithering, except we're intentionally pretending like each frame has a limited amount of precision, and only when averaged together is the original image re-created.

I've created a proof of concept (source) of this. It loads the image into a <canvas> and generates a "positive" and "negative" frame out of it. The positive frame has a random offset added to each pixel's RGB components, while the negative one has it subtracted. When displayed in quick sequence (requestAnimationFrame is used to do this every time the screen refreshes) the two offsets should cancel out, and the resulting image should re-appear.

Source Temporal dithering
Source Temporal dithering1
Positive frame Negative frame
Positive frame Negative frame

The resulting flicker is unfortunate, but perhaps that only enhances the furtiveness of an image that will disappear in a few seconds. It also seemed fitting to include Lenna as a source image, given its origin.

Obviously this technique is meant to deter only casual attempts at screen capture. Beyond the analog hole, screen recording software (albeit not an issue on non-jailbroken iOS devices) can easily reconstruct the original image.

Potential areas of exploration are doing the offsets in a different color space (RGB is not linear) and using more than two frames. One option for generating more frames is to keep making random positive and negative pairs. That way repeated screen captures are less likely to yield a pair that can be combined to reconstruct the original image. Another option for generating more frames is to create three or more images that need to be averaged together to yield the original. However, that will result in more image degradation, since the frames are less likely to be perceived as one image; persistence of vision lasts for 1/25th of a second, which is between 2 and 3 frames at 60Hz.

Update: See also the discussion on Hacker News.

  1. The embedded example in this blog post is rendered as an animated GIF. Due to clamping of GIF frame rates, lack of precision (the delay is specified as an integer counting hundredths of a second) and guessing at the screen's refresh rate (it assumes 60Hz), it will exhibit even more flicker than the programmatic version.

20 Comments

Cool idea to solve this problem.
I've used similar approach to protect CAPTCHA images before which was named "sparkle" for obvious reasons. Because the approach was too easy to counter, I came up with another approach named "peekaboo" which can also be applied to Snapchat-like applications.

Peekaboo works by displaying only a small circular area around mouse cursor or finger. To get the whole image, hacker has to record many images and stitch them together. Consider its voyeuristic nature to be a feature. ;-)
You can dump each window render frame as an image using a program like FRAPS...
You could take a few screenshots and chances would be that you've captured each frame. From there it's trivial to reconstruct.
What you can see, you can screenshot...
Interesting idea, but there's a much simpler solution to the "problem": if you don't want somebody to keep a copy of an image, don't give it to them!
When will anyone accept that once something is on the internet, it is published. On the other hand, I can see this being used fir the next release of 'blu-ray'.

In saying all that, it's quite interesting, and algorithms which can reverse such an effect could have some wider image-processing applications.
The only solution is to distort image by adding watermark.
Interesting approach.

I took 3 screenshots and ended up with the positive and negative versions. Shouldn't be too difficult for a screen scraping program to be created that merges the two images.

It's done in JavaScript, and my browser is in control of all JavaScript provided to it.... so I can also just parse the code for the source image and then grab it.

What about people with medical disorders such as epilepsy? Seems like this approach could trigger seizures with all that flashing.
Great proof of concept!

Unfortunately now my eyes hurt after just 20 seconds of looking at that.
The most perfect workaround is a photo camera - if the exposure is greater than the frequency of the changes, then it'll capture the correct merged image.
Great work guys. Someone creates a great proof of concept and all you can do is whine about possible workarounds.

Sure WE can work around this issue, but if I am sexting with someone who is a computeretarded they might not think of a work around. Even if they could, so what? Whining here about there being work arounds is like claiming that bike locks are useless because there are workarounds, they serve a certain purpose in making it more difficult to steal the bike. Same goes for this.
This is a fantastic ideas for online security companies who encounter people stealing security seal images and posting them on their site.

Think hackerproof or SSL secured seals that could easily be copied and pasted on another site. With this, it adds a painful step to overcome if you want to steal a seal image and post it on your own site.

Folks, remember, nothing is 100% fool-proof, but you need to think of making your content, product, or feature a little bit harder to steal than the rest of the competition to protect yourself.
Interesting idea. I think it will give people a headache though, unfortunately.

Another approach would be for the mobile OS makers to grant apps the right to disable screenshots, though I suppose that would open a pandora's box of copyright abuses (e.g. apps which suddenly don't allow users to make fair use of content).
This idea is stupid. Any of you numnuts ever hear of long exposure mode on a digital camera? It will not only defeat this, but also Snapchat and Poke as well.
Maybe time for a user script hiding anonymous comments by content regexp match, defaulting to /stupid|dumb|expletives/i; those are predictably very likely to just be junk noise.

I got curious if the animation jitteriness is easily fixed by making a requestAnimationFrame loop that flips the images and runs in full 60Hz, instead of whatever drives the animation now. Or is this already the case?
Johan: I already use requestAnimationFrame. Not sure why frames are occasionally skipped, very little work is done per-frame (for example, no JS objects are created or destroyed, so there should be no GC).
The jitter might be temporal aliasing, caused by the animation frame-rate running out of sync with the display device refresh rate. Native software can wait for a v-sync before drawing the next frame, thus latching the image switching speed to a stable frequency. The browser canvas element, however, seems to suffer from a lack of v-sync hooks.

A possible workaround: Create a double-buffer. Two divs, each contains a canvas, one pixelated variation image per canvas, setTimeout alternatively toggling div visibility. This should allow animation without canvas functions in the inner loop.

Side note: Smooth animation might depend on knowning the user's monitor refresh rate. 60Hz = 16.666ms timer, 75Hz = 13.333ms, 100Hz = 10ms. Can we get the refresh rate via user agent or javascript?
That's pretty much how it works now. The canvas is only used for image processing; it outputs into two img tags that are swapped between by changing their zIndex. The swap happens via requestAnimationFrame, which is supposed to track the screen's refresh rate. I can only assume that the flicker is caused by browser or graphics driver bugs; skipped frames are much more noticeable here than in the usual animation.
Is interpolation a useful addition to this?
As far as the complaint that we're all picking the solutions apart, that's what hacking IS, and if you don't have an answer for a workaround that took three smart guys two days to come up with, then you don't have a robust enough concept to bring to market yet. To catch a thief, you must think like a thief, etc.

Post a Comment