├── wave.gif
├── examples
├── index.html
├── audio-element.js
├── microphone.js
└── custom.js
├── LICENSE
├── package.json
├── src
└── index.js
└── README.md
/wave.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mathiasvr/audio-oscilloscope/HEAD/wave.gif
--------------------------------------------------------------------------------
/examples/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Oscilloscope
5 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
37 |
38 |
--------------------------------------------------------------------------------
/examples/audio-element.js:
--------------------------------------------------------------------------------
1 | function startExample () {
2 | const audioContext = new window.AudioContext()
3 |
4 | // setup canvas
5 | const canvas = document.createElement('canvas')
6 | canvas.width = window.innerWidth
7 | canvas.height = window.innerHeight - 50
8 | document.body.appendChild(canvas)
9 |
10 | // setup audio element
11 | const audioElement = document.createElement('audio')
12 | audioElement.controls = true
13 | audioElement.autoplay = true
14 | audioElement.src = 'audio.mp3'
15 | document.body.appendChild(audioElement)
16 |
17 | // create source from html5 audio element
18 | const source = audioContext.createMediaElementSource(audioElement)
19 |
20 | // attach oscilloscope
21 | const scope = new Oscilloscope(source)
22 |
23 | // reconnect audio output to speakers
24 | source.connect(audioContext.destination)
25 |
26 | // customize drawing options
27 | const ctx = canvas.getContext('2d')
28 | ctx.lineWidth = 2
29 | ctx.strokeStyle = '#ffffff'
30 |
31 | // start default animation loop
32 | scope.animate(ctx)
33 | }
34 |
--------------------------------------------------------------------------------
/examples/microphone.js:
--------------------------------------------------------------------------------
1 | function startExample () {
2 | // shim
3 | navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia ||
4 | navigator.mozGetUserMedia || navigator.msGetUserMedia
5 |
6 | const audioContext = new window.AudioContext()
7 |
8 | // setup canvas
9 | const canvas = document.createElement('canvas')
10 | canvas.width = window.innerWidth
11 | canvas.height = window.innerHeight
12 | document.body.appendChild(canvas)
13 |
14 | // customize drawing options
15 | const ctx = canvas.getContext('2d')
16 | ctx.lineWidth = 2
17 | ctx.strokeStyle = '#ffffff'
18 |
19 | // get user microphone
20 | const constraints = { video: false, audio: true }
21 | navigator.getUserMedia(constraints, function (stream) {
22 | const source = audioContext.createMediaStreamSource(stream)
23 |
24 | // attach oscilloscope
25 | const scope = new Oscilloscope(source)
26 |
27 | // start default animation loop
28 | scope.animate(ctx)
29 | }, function (error) {
30 | console.error('getUserMedia error:', error)
31 | })
32 | }
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015-2018 Mathias Rasmussen
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "oscilloscope",
3 | "version": "1.3.0",
4 | "description": "Waveform audio visualizer for the HTML5 canvas",
5 | "source": "src/index.js",
6 | "main": "dist/oscilloscope.js",
7 | "module": "src/index.js",
8 | "dependencies": {},
9 | "devDependencies": {
10 | "microbundle": "^0.12.4",
11 | "rimraf": "^3.0.2",
12 | "standard": "^16.0.2",
13 | "webpack": "^5.4.0",
14 | "webpack-cli": "^4.2.0"
15 | },
16 | "scripts": {
17 | "test": "standard src/*.js && standard --global Oscilloscope examples/*.js",
18 | "clean": "rimraf dist/*",
19 | "build": "microbundle --format=cjs",
20 | "bundle": "webpack --entry=./dist/oscilloscope.js --devtool=source-map --output-library=Oscilloscope --output-filename=oscilloscope.min.js",
21 | "prepare": "npm run clean && npm run build && npm run bundle"
22 | },
23 | "repository": {
24 | "type": "git",
25 | "url": "git+https://github.com/mathiasvr/audio-oscilloscope.git"
26 | },
27 | "keywords": [
28 | "oscilloscope",
29 | "waveform",
30 | "visualization",
31 | "frequency",
32 | "AudioNode",
33 | "MediaStream"
34 | ],
35 | "author": "Mathias Rasmussen",
36 | "license": "MIT",
37 | "bugs": {
38 | "url": "https://github.com/mathiasvr/audio-oscilloscope/issues"
39 | },
40 | "homepage": "https://github.com/mathiasvr/audio-oscilloscope#readme",
41 | "directories": {
42 | "example": "examples"
43 | },
44 | "files": [
45 | "dist"
46 | ]
47 | }
48 |
--------------------------------------------------------------------------------
/examples/custom.js:
--------------------------------------------------------------------------------
1 | function startExample () {
2 | const audioContext = new window.AudioContext()
3 |
4 | // setup canvas
5 | const canvas = document.createElement('canvas')
6 | canvas.width = window.innerWidth
7 | canvas.height = window.innerHeight
8 | document.body.appendChild(canvas)
9 |
10 | // setup audio element
11 | const audioElement = document.createElement('audio')
12 | audioElement.autoplay = true
13 | audioElement.src = 'audio.mp3'
14 | document.body.appendChild(audioElement)
15 |
16 | // create source from html5 audio element
17 | const source = audioContext.createMediaElementSource(audioElement)
18 |
19 | // attach oscilloscope
20 | const scope = new Oscilloscope(source)
21 |
22 | // reconnect audio output to speakers
23 | source.connect(audioContext.destination)
24 |
25 | const ctx = canvas.getContext('2d')
26 | ctx.lineWidth = 3
27 | ctx.shadowBlur = 4
28 | ctx.shadowColor = 'white'
29 |
30 | // custom animation loop
31 | function drawLoop () {
32 | ctx.clearRect(0, 0, canvas.width, canvas.height)
33 |
34 | const centerX = canvas.width / 2
35 | const centerY = canvas.height / 2
36 |
37 | // draw circle
38 | ctx.beginPath()
39 | ctx.arc(centerX, centerY, 100, 0, 2 * Math.PI, false)
40 | ctx.fillStyle = 'yellow'
41 | ctx.fill()
42 |
43 | // draw three oscilloscopes in different positions and colors
44 | ctx.strokeStyle = 'lime'
45 | scope.draw(ctx, 0, 0, centerX, centerY)
46 |
47 | ctx.strokeStyle = 'cyan'
48 | scope.draw(ctx, centerX, 0, centerX, centerY)
49 |
50 | ctx.strokeStyle = 'red'
51 | scope.draw(ctx, 0, centerY, undefined, centerY)
52 |
53 | window.requestAnimationFrame(drawLoop)
54 | }
55 |
56 | drawLoop()
57 | }
58 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | export default class Oscilloscope {
2 | constructor (source, options = {}) {
3 | if (!(source instanceof window.AudioNode)) {
4 | throw new Error('Oscilloscope source must be an AudioNode')
5 | }
6 |
7 | if (source instanceof window.AnalyserNode) {
8 | this.analyser = source
9 | } else {
10 | this.analyser = source.context.createAnalyser()
11 | source.connect(this.analyser)
12 | }
13 |
14 | if (options.fftSize) { this.analyser.fftSize = options.fftSize }
15 | this.timeDomain = new Uint8Array(this.analyser.fftSize)
16 | this.drawRequest = 0
17 | }
18 |
19 | // begin default signal animation
20 | animate (ctx, x0, y0, width, height) {
21 | if (this.drawRequest) {
22 | throw new Error('Oscilloscope animation is already running')
23 | }
24 | this.ctx = ctx
25 | const drawLoop = () => {
26 | ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
27 | this.draw(ctx, x0, y0, width, height)
28 | this.drawRequest = window.requestAnimationFrame(drawLoop)
29 | }
30 | drawLoop()
31 | }
32 |
33 | // stop default signal animation
34 | stop () {
35 | if (this.drawRequest) {
36 | window.cancelAnimationFrame(this.drawRequest)
37 | this.drawRequest = 0
38 | this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height)
39 | }
40 | }
41 |
42 | // draw signal
43 | draw (ctx, x0 = 0, y0 = 0, width = ctx.canvas.width - x0, height = ctx.canvas.height - y0) {
44 | this.analyser.getByteTimeDomainData(this.timeDomain)
45 | const step = width / this.timeDomain.length
46 |
47 | ctx.beginPath()
48 | // drawing loop (skipping every second record)
49 | for (let i = 0; i < this.timeDomain.length; i += 2) {
50 | const percent = this.timeDomain[i] / 256
51 | const x = x0 + (i * step)
52 | const y = y0 + (height * percent)
53 | ctx.lineTo(x, y)
54 | }
55 |
56 | ctx.stroke()
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # oscilloscope
2 | [](https://npm.im/oscilloscope)
3 | 
4 | 
5 | [](https://mvr.mit-license.org)
6 |
7 | Waveform audio visualizer for the HTML5 canvas.
8 |
9 | 
10 |
11 | ## install
12 | Import the module and bundle it for the browser with your favorite module bundler,
13 | ```
14 | $ npm install oscilloscope
15 | ```
16 |
17 | or include it directly in your HTML:
18 | ```html
19 |
20 | ```
21 |
22 | ## example
23 | ```javascript
24 | var Oscilloscope = require('oscilloscope')
25 |
26 | var audioContext = new window.AudioContext()
27 |
28 | // create source from html5 audio element
29 | var source = audioContext.createMediaElementSource(audioElement)
30 |
31 | // attach oscilloscope
32 | var scope = new Oscilloscope(source)
33 |
34 | // start default animation loop
35 | scope.animate(canvas.getContext("2d"))
36 | ```
37 |
38 | See the [examples](examples) folder for more details on how to customize the animation.
39 |
40 | ## usage
41 | ### `scope = new Oscilloscope(source [, { fftSize: 2048 }])`
42 | Supply an [AudioNode](https://developer.mozilla.org/en-US/docs/Web/API/AudioNode) as `source`.
43 |
44 | Optionally set the `fftSize` property of the internal [AnalyzerNode](https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/fftSize).
45 | This can be used to adjust the smoothness or resolution of the animated oscilloscope.
46 |
47 | For more control supply an AnalyzerNode as `source`.
48 |
49 | #### `scope.draw(context [, x, y, width, height])`
50 | Draw oscilloscope to a canvas `context`, and optionally set position and dimensions.
51 |
52 | #### `scope.animate(context [, x, y, width, height])`
53 | Start a basic animation loop that redraws the oscilloscope using the `.draw()` method.
54 |
55 | #### `scope.stop()`
56 | Stop the animation loop started by the `.animate()` method.
57 |
--------------------------------------------------------------------------------