├── README.md ├── css └── styles.css ├── index.html └── js └── main.js /README.md: -------------------------------------------------------------------------------- 1 | # speech-jammer 2 | Speech Jammer using getUserMedia and Web Audio API 3 | 4 | Point a web server to the folder 5 | -------------------------------------------------------------------------------- /css/styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | font-family: 'Roboto Slab', sans-serif; 4 | font-weight: 100; 5 | margin: 0; 6 | padding: 0; 7 | } 8 | 9 | html { 10 | height: 100%; 11 | } 12 | 13 | body { 14 | margin: 0; 15 | padding: 0; 16 | background-color: #232323; 17 | color: #ffffff; 18 | line-height: 20px; 19 | height: 100%; 20 | overflow: hidden; 21 | font-family: 'Roboto Slab', sans-serif; 22 | font-size: 13px; 23 | font-weight: 100; 24 | } 25 | 26 | b { 27 | font-weight: bold; 28 | } 29 | 30 | div.panel { 31 | position: absolute; 32 | left: 50%; 33 | top: 50%; 34 | transform: translate3d(-50%, -50%, 0); 35 | line-height: 1.4em; 36 | font-size: 3vw; 37 | white-space: nowrap; 38 | } 39 | 40 | note { 41 | font-size: 2vw; 42 | display: block; 43 | line-height: 1.4em 44 | } 45 | 46 | .controls {} 47 | 48 | .controls p { 49 | font-size: 2vw; 50 | position: relative; 51 | margin: 20px 0; 52 | } 53 | 54 | .controls input[type=range] { 55 | position: absolute; 56 | left: 30%; 57 | right: 0; 58 | top: 50%; 59 | margin-top: -10px; 60 | width: 70%; 61 | } 62 | 63 | .click { 64 | position: absolute; 65 | left: 0; 66 | top: 0; 67 | right: 0; 68 | bottom: 0; 69 | background-color: rgba(0, 0, 0, .9); 70 | color: white; 71 | z-index: 100; 72 | width: 100%; 73 | height: 100%; 74 | pointer-events: auto; 75 | } 76 | 77 | .click p { 78 | position: absolute; 79 | left: 50%; 80 | top: 50%; 81 | transform: translate3d(-50%, -50%, 0); 82 | text-align: center; 83 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Speech Jammer 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |

Welcome!

We need to run some audio
Click to start

16 |
17 |
18 |

Speech Jammer


19 |

1. Plug your headphones

20 |

2. Allow the browser access to your microphone

21 |

3. Raise the volume of your headphones

22 |

4. Try talking like a sane person

23 |
24 |

Delay (150ms)

25 |

Gain (x1)

26 |
27 | Uses Web Audio API and WebRTC getUserMedia.
Chrome and Firefox only
28 |
29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | function init() { 2 | 3 | window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext; 4 | 5 | var context = new AudioContext(); 6 | 7 | var delayValue = document.getElementById('delayValue'); 8 | var delayRange = document.getElementById('delayRange'); 9 | 10 | var gainValue = document.getElementById('gainValue'); 11 | var gainRange = document.getElementById('gainRange'); 12 | 13 | var gain = context.createGain(); 14 | gain.connect(context.destination); 15 | gain.gain.value = 1; 16 | if (localStorage['gainValue']) { 17 | var v = parseFloat(localStorage['gainValue']); 18 | if (v < 1) v = 1; 19 | if (v > 10) v = 10; 20 | gain.gain.value = v; 21 | gainRange.value = v; 22 | gainValue.textContent = parseFloat(v).toFixed(1); 23 | } 24 | 25 | var delay = context.createDelay(); 26 | delay.connect(gain); 27 | delay.delayTime.value = .15; 28 | if (localStorage['delayValue']) { 29 | var v = parseFloat(localStorage['delayValue']); 30 | if (v < 0) v = 0; 31 | if (v > 1) v = 1; 32 | delay.delayTime.value = v; 33 | delayRange.value = v * 1000; 34 | delayValue.textContent = v * 1000; 35 | } 36 | 37 | navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; 38 | 39 | var mediaStreamSource; 40 | 41 | if (navigator.getUserMedia) { 42 | navigator.getUserMedia({ audio: true }, 43 | function(stream) { 44 | mediaStreamSource = context.createMediaStreamSource(stream); 45 | mediaStreamSource.connect(delay); 46 | }, 47 | function(err) { 48 | console.log("The following error occured: " + err.name); 49 | } 50 | ); 51 | } else { 52 | console.log("getUserMedia not supported"); 53 | } 54 | 55 | delayRange.addEventListener('input', function(e) { 56 | 57 | delay.delayTime.value = this.value / 1000; 58 | delayValue.textContent = this.value; 59 | localStorage['delayValue'] = this.value / 1000; 60 | 61 | }); 62 | 63 | gainRange.addEventListener('input', function(e) { 64 | 65 | gain.gain.value = this.value; 66 | gainValue.textContent = parseFloat(this.value).toFixed(1); 67 | localStorage['gainValue'] = this.value; 68 | 69 | }); 70 | 71 | } 72 | 73 | window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext; 74 | 75 | var overlay = document.getElementById('overlay'); 76 | overlay.addEventListener('click', onClick); 77 | 78 | var test = new AudioContext(); 79 | if (test.state === 'running') { 80 | onClick(); 81 | } 82 | 83 | function onClick() { 84 | overlay.removeEventListener('click', onClick); 85 | overlay.style.display = 'none'; 86 | init(); 87 | } --------------------------------------------------------------------------------