Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Pixelating Live with SVG (meyerweb.com)
74 points by surprisetalk on Dec 24, 2023 | hide | past | favorite | 31 comments


Here's the closest I could come up with: https://codepen.io/dsmmcken/pen/rNRaOJE

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.


I came across this SVG pixellate filter on Codepen, by Taylor Hunt, a while back. https://codepen.io/tigt/pen/aZYqrg - it works, but I'm not sure how it works. SVG filter code is just weird!


The filter first creates pattern of 2x2 black squares, spaced by 8 pixels vertically and horizontally.

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.


'Kay, I don't know if this comes anywhere close to what the OP wants, but this sort of live browser tab manipulation is possible to do using a mix of a a canvas element and the browser's Screen Capture API[1] (plus my JS canvas library, once I merge and publish the required changes into its next release[2]).

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


Not sure why they’re reaching for SVG, but I don’t think there’s a built-in solution. This would be fairly simple to do by rendering to a canvas, and then doing the pixelation in JavaScript (or maybe WebGL). This would be expensive and won’t be super performant, so best as a one-time thing on static content, not something you want to do on top of an animated page.


They are using SVG because SVG filters can be used in CSS too. You can even apply filters to real-time video playback. I have a video player project where I’ve worked on adding filters for black and white, retro, etc.

You can apply them to any element or even the entire page.

https://developer.mozilla.org/en-US/docs/Web/CSS/filter#synt...


Here is a demo page I had for trying out filters to show the effect: https://svg-video-filters.glitch.me/


Nice, the morph filter is tantalizingly close to what the poster is looking for. (I’m sure you know already, but unfortunately that page doesn’t work on iOS.) Maybe there’s a ‘nearest neighbor’ or ‘box filter’ blur in SVG, but I’m not aware of one. I googled and see other people looking for the same effect and resorting to canvas and hand-crafted image processing. It might be doable and reasonably performant with a WebGL shader.


The reason most filters are so damn slow in Firefox here is because this is grabbing each frame and processing them in the CPU, right?

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)


Maybe screenshot + canvas with pointer-events none to fake live. Since it would be a perfect overlay no one should be able to tell the difference


You could exchange the font with one that has the illegible blocks you want as letters? There are web sites that help with creating such a font.


I quite like the idea, I just don't understand why SVG to render the pixel output. Why not canvas, or another progammable raster container?

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.


You can use SVG filters in CSS to apply to any element or even the whole page. Check out this demo page I have for trying SVG filters on a video. https://svg-video-filters.glitch.me/


SVG can contain images, including data URL ones, what's missing?


You're right of course, it just feels like a waste of the spec to use it only to embed raster.

Is there no gradient style that could be used to encode pixels, directly using the schema?


What do you mean by "waste of the spec"?


Its a vector graphic spec with support for raster embeddings. Using it to hold primarily raster data feels like an underutilisation of the vector capabilities.

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.


That seems like it would be a lot of duplication of effort. A vector format that also has its own raster codec (and having to deal with compression, different metadata, etc?). Seems like the worst of both worlds, vs just using Canvas to output programmable rasters. Different implementations/browsers already have enough trouble outputting SVG as it is, making it even more complex/powerful would be counterproductive, I think...

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.


You could downscale the screenshot and use an `<img>` with `image-rendering: pixelated` to display it.


Looks amazing, love it.

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 aware I could take raster screenshots of pages and then manipulate them in an image editor. I don’t want to do that, though — I want to pixelate live. For reasons.

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?


You can use SVG filters in CSS to apply to any element or even the whole page. Check out this demo page I have for trying SVG filters on a video. https://svg-video-filters.glitch.me/


Would the easiest route for text be too make your own WebFont in both serif / san-serif which are "blurred/pixelated" to over ride font-family tags in CSS? Then do the remainder of the image blurring in the technique already used?


I played around with this for at least an hour. Countless rounds back and for with ChatGPT on SVG. I'm no closer to something better than the examples in the StackOverflow thread. I hope he figures it out though.


Amateur attempt at writing SVG filters with a grounding in DSP/image processing:

  <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.


This is pretty much what the author is looking for. Here is my version with some optimizations and using 5x5 cells:

  <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>


That is the closest so far I think. It is interesting that the filter blurs raster images differently than it does a block of text. I applied your filter on this page: https://svg-video-filters.glitch.me/index-2.html. I had to add `x="0" y="0"` to <filter> to make it work.

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 attributes in the tiling pattern are for isolating the middle pixel in every cell. Consider a 5x5 cell. The middle pixel would be x=2, y=2. The width and height being 1 is so that we only isolate a single pixel.

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


That is great! Trying it out in my test harness didn't work, but apparently there was an extra <def> surrounding all of my filter definitions that caused a problem, but only with this one filter. Thanks for the explanation!


Because they won’t say why I’m curious. My (naive) guess is some type of dynamic paywall generation. Interesting problem and solution either way and love seeing a clever use for SVG’s.


It seemed to me more like he was trying to avoid people bikeshedding his use case and just wanted a solution.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: