Pixelating Live with SVG
meyerweb.com
meyerweb.com
It does a subtle blur, then a subtle sharpen, then repeats the middle pixel of a 3x3 grid. Happy holidays!
Doesn't work on mobile, probably needs some pixel scaling math.
Then it creates a composite of the original image and the just generated pattern, basically coloring the 2x2 squares with colors from the original image.
Finally it blows up the 2x2 squares to 10x10 squares using the feMorphology filter primitive with "dilate" type.
A very clever filter indeed.
This solution[3] shows the modified browser tab in a separate browser tab. I've got no idea whether it's possible to do the same sort of trick in the same tab (but probably not). I also have no idea how secure the Screen Capture API is - I'd get very nervous about doing this sort of thing when looking at my bank's online portal!
[1] https://developer.mozilla.org/en-US/docs/Web/API/Screen_Capt...
[2] https://github.com/KaliedaRik/Scrawl-canvas/pull/57
[3] Youtube video of the effect: https://www.youtube.com/watch?v=hCi6LmKMAo0
You can apply them to any element or even the entire page.
https://developer.mozilla.org/en-US/docs/Web/CSS/filter#synt...
Or the implementations guarantee that all and every SVG filter is done fully in GPU? (I recall Firefox had a push to do more work on GPU with webrender, citing game engines as inspiration, but I reckon this is a hard problem)
As for SVG itself, I think it needs a new primitive shape for holding a pixel field. I've seen too many point plots saved to SVG that take a million years to render.
Is there no gradient style that could be used to encode pixels, directly using the schema?
A PNG would have been better, but a PNG is not programmable, so I lamented the SVG spec not having a programmable bitmap, since lots of people use it for plotting data.
There are also specialist tools for dealing with web raster layers, like in GIS: https://openlayers.org/en/latest/examples/raster.html
This just shouldn't be a job for SVG, IMO.
Unfortunately, the Codepen doesn't work on Safari (iOS and macOS both), and from my perspective that's absolutely expected when dealing with SVGs: standard is wild, you never know how exactly it's going to be rendered.
I'm totally unclear what the author means by "live".
Is it OK to take a static screenshot and pixelate it as long as it's all inside of the browser tab, rather than pasted into an image editor?
Or do they want to be able to be able to select text, have a blinking cursor, type in text fields, etc. and have all that happen behind the pixelation filter?
<div id="comparator">
<img src="https://meyerweb.com/eric/thoughts/wp-content/uploads/wikipedia-home.png" alt="">
<img src="https://meyerweb.com/eric/thoughts/wp-content/uploads/wikipedia-home.png" alt="">
</div>
<svg xmlns='http://www.w3.org/2000/svg'>
<filter id='pixelate' x='0' y='0'>
<!-- <feComposite result="box-blur"/> -->
<feConvolveMatrix order="3" kernelMatrix="1 1 1 1 1 1 1 1 1" result="box-blur" />
<!-- generate tiling pattern -->
<feFlood x='3' y='3' height='1' width='1'/>
<feComposite width='3' height='3'/>
<feTile result='tiles'/>
<!-- apply tiling pattern -->
<feComposite in='box-blur' operator='in'/>
<!-- expand pixels to (2r + 1) large -->
<feMorphology operator='dilate' radius='1'/>
</filter>
</svg>
The feConvolveMatrix is only needed if you want input pixel averaging rather than point sampling. (It automatically divides by the sum of taps.)Note that:
- I'm told that feConvolveMatrix becomes slower for higher orders. For composite input sizes (like powers of 2), you can instead combine multiple passes of sampling with increasing amounts of zero-spacing between taps (the other meaning of dilation LMAO). I don't know if browsers will optimize the filter to skip processing input data, to make the filtering runtime O(log n) rather than O(n^2).
- I don't know any way to make <feMorphology operator='dilate'> expand point-sampled images to even pixel sizes. You may need to start with a different source point-sampling filter.
<div id="comparator">
<img src="https://meyerweb.com/eric/thoughts/wp-content/uploads/wikipedia-home.png" alt="">
<img src="https://meyerweb.com/eric/thoughts/wp-content/uploads/wikipedia-home.png" alt="">
</div>
<svg xmlns='http://www.w3.org/2000/svg'>
<filter id='pixelate'>
<!-- Perform two 1D blurs (vertical and horizontal pass). This is a more efficient way of doing a 2D box blur -->
<feConvolveMatrix order="5 1" kernelMatrix="1 1 1 1 1"/>
<feConvolveMatrix order="1 5" kernelMatrix="1 1 1 1 1" result="blur"/>
<!-- Generate tiling pattern -->
<feFlood x="2" y="2" height="1" width="1"/>
<feComposite width="5" height="5"/>
<feTile/>
<!-- Combine tiling pattern and blurred image -->
<feComposite in="blur" operator="in"/>
<!-- Dilate each pixel to fill each cell of our pixelation grid -->
<feMorphology operator="dilate" radius="2"/>
</filter>
</svg>What are the attributes in the tiling patterned supposed to do? Changing them seemed to have no effect on the image in my test page.
The reason for isolating the middle pixel is because it is the only pixel in the cell that contains the average color of the cell (after box blurring). Once isolated, we can use feMorphology to dilate this pixel so that it fills the cell.
Here is a React version to more clearly demonstrate the relationship between cell size and all of the attributes: https://codepen.io/tommyquant/pen/yLwyoGO