index.html (10286B)
1 <link rel="stylesheet" href="./styles.css"/> 2 3 4 <h1>Interference effects on image zoom</h1> 5 <p class="page-intro"> 6 A playful exploration into the world of image rendering. 7 </p> 8 <p> 9 I recently discovered that pixel interference effects can occur by zooming 10 in on gray image areas. This got me thinking: Can we construct an image 11 that initially looks uniformly gray but reveals information on specific 12 zoom levels? Is it maybe even possible to show different grayscale images 13 depending on the zoom level? 14 </p> 15 <p> 16 The following tests show client-side generated images using the 17 <code><canvas></code> element. However the image data is not manipulated 18 during zoom - just the CSS <code>transform: scale()</code> property changes. 19 The demos need pixel perfect sizes to work. Therefore the images could be too 20 small or too big on some devices - I apologize. 21 </p> 22 <p> 23 Please also note that I do not have any prior knowledge in the field of 24 image rendering. I was just playing around and documenting things I found 25 interesting. I hope you find this little tour interesting as well :). 26 </p> 27 28 29 <h2>The interference effect</h2> 30 <p> 31 The first image is just a checkerboard pattern on pixel scale (this is 32 clearly visible on <a class="demo1-checker">2x</a>). When zooming in the 33 image pixel grid and the screen pixel grid are misaligned creating 34 interference effects in both dimensions. They are especially strong in the 35 area of 36 <a class="demo1-checker">0.5x</a>, 37 <a class="demo1-checker">0.25x</a>, 38 <a class="demo1-checker">0.1667x</a> and 39 <a class="demo1-checker">0.125x</a>. 40 These turn out to be the whole number parts of 1/2. This makes sense: For 41 two adjacent screen pixels the distance of the image pixels is a multiple 42 of 2 which locally creates a uniformly colored area. 43 </p> 44 <!-- demo1-checker --> 45 <div class="js-container"> 46 [If you see this text your browser unfortunately does not support 47 Javascript.] 48 </div> 49 <p> 50 Another observation about browser differences can be made here: In Chromium 51 I can zoom smoothly at this microscopic scale, Firefox only allows zoom 52 steps. Interestingly for every zoom step the image size is an integer 53 multiple of 1/2 of the interference period. I do not know why. Also on 54 Firefox for Android there are graphic glitches across the whole screen for 55 some zoom levels. I guess this is a graphics driver issue of my device but 56 I am not sure. 57 </p> 58 <p> 59 On Chromium the image is completely black on some zoom levels like 60 <a class="demo1-checker">0.5x</a>. However this bug does not occur when 61 opening the image in a new tab and zooming to 62 <a class="demo1-checker">0.5x</a>. 63 </p> 64 <p> 65 For the second test I inverted the checkerboard pattern for some areas of 66 the image. There are zoom levels like <a class="demo2-checkerinvert">1x</a> 67 where just parts of the border of the shape are visible. For other zoom 68 levels like <a class="demo2-checkerinvert">0.5x</a> the whole shape area is 69 clearly visible. 70 </p> 71 <!-- demo2-checkerinvert --> 72 <div class="js-container"> 73 [If you see this text your browser unfortunately does not support 74 Javascript.] 75 </div> 76 77 78 <h2>Other types of colorings</h2> 79 <p> 80 Let's explore some other types of local colorings. I chose uniform gray and 81 random pixel values for the next demo. As expected the gray area does not 82 show any interference effects at all. The random area on the other hand has 83 them but they are hardly visible. You can see them if you keep the rough 84 slider at <a class="demo3-grayrandom">1x</a> and just move the fine slider. 85 </p> 86 <!-- demo3-grayrandom --> 87 <div class="js-container"> 88 [If you see this text your browser unfortunately does not support 89 Javascript.] 90 </div> 91 <p> 92 Now let's combine different types of colorings. The random noise seems to 93 be too different but a combination of checkerboard and gray could work. It 94 does in fact: For <a class="demo4-checkergray">0.999x</a> the difference is 95 just barely visible on Firefox and invisible on Chromium. 96 </p> 97 <!-- demo4-checkergray --> 98 <div class="js-container"> 99 [If you see this text your browser unfortunately does not support 100 Javascript.] 101 </div> 102 <p> 103 To hide the remaining interference artifacts we bring the random noise back 104 and add it to our color values. Now things start to get really interesting. 105 Let's just look at the zoom levels 106 <a class="demo5-checkergrayrandom">0.999x</a> and 107 <a class="demo5-checkergrayrandom">0.5x</a> for the next two demos. 108 </p> 109 <p> 110 On <a class="demo5-checkergrayrandom">0.5x</a> the square is sometimes 111 visible and sometimes invisible. After a bit of testing I found out that it 112 depends on the parity of the image width. I cannot explain it but I also do 113 not find it implausible that these interference effects change drastically 114 on size parity. 115 </p> 116 <p> 117 The real weirdness starts on <a class="demo5-checkergrayrandom">0.999x</a>. 118 On Firefox the square is visible for images smaller or equal to 500px in 119 size and invisible for larger ones. I suppose Firefox uses different 120 rendering techniques for different image sizes. On Chrome the square is 121 visible if and only if it is also visible on 122 <a class="demo5-checkergrayrandom">1x</a> 123 - this means it is visible for even image sizes and invisible for odd 124 sizes. 125 </p> 126 <!-- demo5-checkergrayrandom --> 127 <div class="js-container"> 128 [If you see this text your browser unfortunately does not support 129 Javascript.] 130 </div> 131 <!-- demo5-checkergrayrandom --> 132 <div class="js-container"> 133 [If you see this text your browser unfortunately does not support 134 Javascript.] 135 </div> 136 137 138 <h2>Invisibility on 1x</h2> 139 <p> 140 It would be really cool to create an image that is invisible on <a>1x</a> 141 but appears on zoom. This did already work but just in Chromium. My first 142 idea for this was to hide the interference area in a random area and match 143 the grayscale level: 144 </p> 145 <div class="js-container"> 146 [If you see this text your browser unfortunately does not support 147 Javascript.] 148 </div> 149 <p> 150 This does work on Firefox on my specific screen. The difference is however 151 visible in other browsers and on Firefox on other devices. 152 </p> 153 <p> 154 To create a truly invisible interference area my next idea was to rearrange 155 the pixels according to their color. This ensures equal color distribution 156 inside and outside the interference area. It is however rather complicated 157 to implement. The same effect can be achieved with random colors using 158 uneven probabilities. For a dark pixel on the checkerboard we generate two 159 random color values and take the smaller one. This is equivalent to 160 imagining a neighboring light pixel and swapping their values if necessary. 161 </p> 162 <p> 163 Now the effect really works consistent across devices and screens! Try 164 zooming to <a class="demo6-reordering">0.5x</a> for example. 165 </p> 166 <!-- demo6-reordering --> 167 <div class="js-container"> 168 [If you see this text your browser unfortunately does not support 169 Javascript.] 170 </div> 171 <p> 172 Now if we think in terms of probability distributions this process can be 173 easily generalized from hidden black-and-white to grayscale images. Try the 174 following example on <a class="demo7-reordering-grayscale">0.5x</a>. 175 Unfortunately the interference patterns are only clearly visible in the 176 center of the image as you can see at the end of the paint brush. (This is 177 sometimes not the case on Chromium.) 178 </p> 179 <!-- demo7-reordering-grayscale --> 180 <div class="js-container"> 181 [If you see this text your browser unfortunately does not support 182 Javascript.] 183 </div> 184 <p> 185 It would be really cool if the whole area could be used for hidden images. 186 However i think that is impossible. There are always spots of destructive 187 interference and for <a class="demo7-reordering-grayscale">0.5x</a> and its 188 whole number parts these spots are already at the border of the image. 189 </p> 190 <p> 191 Using the following file picker you can try the effect yourself. I respect 192 your privacy so all images are processed client-side. You can check that in 193 the network tab in the debugging tools of your browser. Please choose a 194 square image - implementing and exploring other aspect ratios is left as an 195 idea to the reader. 196 </p> 197 <!-- custom file upload --> 198 <div class="js-container"> 199 [If you see this text your browser unfortunately does not support 200 Javascript.] 201 </div> 202 203 204 <h2>Colored images</h2> 205 <p> 206 Since RGB displays have separate LEDs for the three color components this 207 process should be easily generalizable to color images by just repeating it 208 three times per pixel. It works, indeed 209 (try <a class="demo8-color">0.5x</a>). 210 </p> 211 212 <!-- demo8-color --> 213 <div class="js-container"> 214 [If you see this text your browser unfortunately does not support 215 Javascript.] 216 </div> 217 <p> 218 As above, you can try it yourself: 219 </p> 220 <!-- custom file upload 2 --> 221 <div class="js-container"> 222 [If you see this text your browser unfortunately does not support 223 Javascript.] 224 </div> 225 226 227 <h2>Ideas for future exploration</h2> 228 <p> 229 Many questions are left unanswered in this text. I chose to highlight some 230 of them: 231 </p> 232 <ul> 233 <li>What happens on aspect ratios different from 1:1?</li> 234 <li>Is it possible to show different images on different zoom levels? 235 If so, how?</li> 236 <li>How can these phenomena be explained? Maybe explanations can be found 237 in the source code of Firefox/Chromium or image rendering libraries. 238 This is unfortunately beyond my abilities.</li> 239 </ul> 240 <p> 241 That's enough for my curiosity. If you are still curious you can find the 242 source code of this page <a href="/git/tongong.net/files.html">here</a>. 243 </p> 244 245 246 <h2>Thank you...</h2> 247 <ul> 248 <li>... <a href="https://mithril.js.org/">mithril.js</a> for keeping the 249 fun in this project by abstracting over the Javascript DOM API</li> 250 <li>... <a href="https://ciechanow.ski/">Bartosz Ciechanowski</a> for being 251 a great inspiration</li> 252 <li>... for reading :)</li> 253 </ul> 254 255 <script src="./main.js"></script>