├── Readme.md ├── index.html ├── screentunes.css └── screentunes.js /Readme.md: -------------------------------------------------------------------------------- 1 | # Screen Tunes 2 | 3 | It turns out that some LCD monitors will emit a tone when patterns of repeating bars are displayed on the screen. 4 | This pattern varies based on the size of the bars. The goal of this project is to eventually be able to play some 5 | music through the screen using a web page. 6 | 7 | I found out about this effect from the Hacker News comments on a page that was about a completely different bar spacing effect 8 | but produced sound out of some monitors (including my external Samsung LCD). 9 | [Some commenters](https://news.ycombinator.com/item?id=8856930) produced theories including that this is caused by 10 | the expansion and contraction of capacitors/inductors in the display as the screen scans down and the black pixels take a different 11 | amount of power than the white pixels. This theory suggests that varying the width of the bars would change the pitch, 12 | which indeed does happen. 13 | 14 | This is mainly intended as an experiment out of curiosity and geekery; it has no practical use. 15 | 16 | ## Technique 17 | 18 | I simply created a full screen canvas where I draw an animated and variably spaced sequence of black and white bars. 19 | Displaying this pattern in a large browser window on some LCD monitors produces a tone. 20 | 21 | ## Plans 22 | 23 | #### Music 24 | 25 | Theoretically this could be used to play simple digital chiptune-like songs. By animating to different bar widths the notes 26 | could be changed at around 60HZ. It might even be possible to play multiple notes at once by either partitioning the screen 27 | vertically into different chunks with different bar widths on each, or perhaps just alternating bar widths at 60HZ might be too 28 | fast for the ear to discern the alternation. 29 | 30 | #### Calibration 31 | 32 | Using the web audio API I could direct users to put a microphone near their screen and do a bar width sweep and record the 33 | frequency and relative amplitude. This could used for both data collection on this phenomenon and to calibrate the playing of 34 | music to emit the correct pitch at the correct amplitude. 35 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Screen Tunes 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |

Screen Tunes

13 | By Tristan Hume 14 |

15 | On some LCD monitors this page will cause the screen to emit a 16 | tone that varies in pitch with the bar height. Maximize window for best results. 17 |

18 |

19 | For more see the Github Page 20 |

21 |

22 | Edit: I kinda managed to get this to play music but it doesn't work very well. If you're ambitious you can download and try out the "music" branch on Github. 23 |

24 |
25 | 26 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /screentunes.css: -------------------------------------------------------------------------------- 1 | * { margin:0; padding:0; } /* to remove the top and left whitespace */ 2 | html, body { width:100%; height:100%; } /* just to be sure these are full screen*/ 3 | canvas { display:block; } /* To remove the scrollbars */ 4 | 5 | /* canvas { */ 6 | /* position: absolute; top: 0px; left: 0px; z-index: -1; */ 7 | /* } */ 8 | 9 | #info { 10 | position: absolute; 11 | top: 10px; left: 10px; 12 | z-index: 100; 13 | 14 | background: #D9E9F5; 15 | width: 350px; 16 | padding: 9px; 17 | border-radius: 5px; 18 | font-family: sans-serif; 19 | } 20 | 21 | p { 22 | margin-top: 5pt; 23 | } 24 | -------------------------------------------------------------------------------- /screentunes.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var canvas; 3 | var ctx; 4 | var start = null; 5 | var t = null; 6 | 7 | var AnimationFrame = (function() { 8 | var FPS = 16.6666666667; // 1000 / 60 = Frames Per Second 9 | var RAF = window.requestAnimationFrame 10 | || window.webkitRequestAnimationFrame 11 | || window.mozRequestAnimationFrame 12 | || window.msRequestAnimationFrame 13 | || window.oRequestAnimationFrame 14 | || function(a) { window.setTimeout(a, FPS); } 15 | var CAF = window.cancelAnimationFrame 16 | || window.webkitCancelAnimationFrame 17 | || window.mozCancelAnimationFrame 18 | || window.msCancelAnimationFrame 19 | || window.oCancelAnimationFrame 20 | || function(a) { window.clearTimeout(a); } 21 | return { 22 | request: function(a) { 23 | RAF(a); 24 | }, 25 | cancel: function(a) { 26 | CAF(a); 27 | } 28 | } 29 | })(); 30 | 31 | function resize() { 32 | canvas.width = window.innerWidth; 33 | canvas.height = window.innerHeight; 34 | /* $("#canvas").css("width", w + "px"); 35 | $("#canvas").css("height", h + "px"); */ 36 | /* render(); */ 37 | } 38 | 39 | function bar(y, height) { 40 | ctx.fillRect(0,y,canvas.width,height); 41 | } 42 | 43 | function bars(spacing, height) { 44 | ctx.fillStyle = "rgb(0,0,0)"; 45 | var y = 0; 46 | var maxy = canvas.height; 47 | while(y < maxy) { 48 | bar(y,height); 49 | y += spacing + height; 50 | } 51 | } 52 | 53 | function render() { 54 | ctx.clearRect(0, 0, canvas.width, canvas.height); 55 | var magic = 5 + (Math.sin(t / 2000)+1)*7; 56 | bars(magic, magic); 57 | } 58 | 59 | function frame(timestamp) { 60 | if (start === null) start = timestamp; 61 | t = timestamp - start; 62 | render(); 63 | AnimationFrame.request(frame); 64 | } 65 | 66 | $(document).ready(function() { 67 | canvas = document.getElementById("main"); 68 | ctx = canvas.getContext("2d"); 69 | $(window).bind("resize", resize); 70 | resize(); 71 | AnimationFrame.request(frame); 72 | }); 73 | })(); 74 | --------------------------------------------------------------------------------