Skip to content

Instantly share code, notes, and snippets.

@steventhebrave
Created September 25, 2017 13:19
Show Gist options
  • Save steventhebrave/7c16a72fb940b05b5e5218390418b5bf to your computer and use it in GitHub Desktop.
Save steventhebrave/7c16a72fb940b05b5e5218390418b5bf to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head><title>SOUND</title></head>
<body>
<div>Frequence: <span id="frequency"></span></div>
<script type="text/javascript">
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var oscillatorNode = audioCtx.createOscillator();
var gainNode = audioCtx.createGain();
var mute = true;
var frequency = 500;
var direction = "";
var volume = 0.8;
var speed = 1;
var grossTune = 5;
var mediumTune = 0.5;
var fineTune = 0.05;
var keys = [
{key:81, direction:"down", tune:grossTune}, //q
{key:87, direction:"up", tune:grossTune}, //w
{key:69, direction:"down", tune:mediumTune},//e
{key:82, direction:"up", tune:mediumTune}, //r
{key:84, direction:"down", tune:fineTune}, //t
{key:89, direction:"up", tune:fineTune} //y
];
oscillatorNode.connect(gainNode);
gainNode.connect(audioCtx.destination)
oscillatorNode.start();
oscillatorNode.frequency.value = frequency;
gainNode.gain.value = 0;
(function manualLoop() {
setTimeout(function() {
manualLoop();
if (direction == "up"){
frequency += speed;
}
if (direction == "down"){
frequency -= speed;
}
oscillatorNode.frequency.value = frequency;
document.getElementById('frequency').innerHTML = frequency.toFixed(2);
}, 40)
}());
document.addEventListener('keydown', function(event) {
if (event.keyCode == 32) { // space bar
if (mute) {
gainNode.gain.value = volume;
mute = false;
} else {
gainNode.gain.value = 0;
mute = true;
}
}
for (var i = keys.length - 1; i >= 0; i--) {
if(event.keyCode == keys[i].key) {
direction = keys[i].direction;
speed = keys[i].tune;
}
}
});
document.addEventListener('keyup', function(event) {
direction = "";
});
</script>
</body>
</html>
@salamantos
Copy link

Google Chrome 79.0.3945.130 gives the following error:

The AudioContext was not allowed to start. It must be resumed (or created) after a user gesture on the page.

@haveboard
Copy link

Added the start button to account for the "If you create your AudioContext on page load, you’ll have to call resume()" issue from
https://developers.google.com/web/updates/2017/09/autoplay-policy-changes#webaudio

<!DOCTYPE html>
<html>
<meta name="viewport" content="width=device-width, user-scalable=no">
<head><title>SOUND</title></head>
<body>
<style>
	button{
		height:100px;
		width:100px;
	}

	body {
		font-family:Verdana, Geneva, sans-serif;
		font-size:medium;
		text-align: center;
	}
</style>
<div id="controls">
	<p>
		<button class="start">Start</button>
	</p>
	<h1>Frequency: <span id="frequency"></span></h1>
	<p>	Gross
		<div>
			<button value="q">Q</button>
			<button value="w">W</button>
		</div>
	</p>
	<p>	Medium
		<div>
			<button value="e">E</button>
			<button value="r">R</button>
		</div>
	</p>
	<p> Fine
		<div>
			<button value="t">T</button>
			<button value="y">Y</button>
		</div>
	</p>
	<p>
		<button value="">Play/Pause</button>
	</p>

</div>
<script type="text/javascript">

// One-liner to resume playback when user interacted with the page.
document.querySelector('button.start').addEventListener('click', function() {
    audioCtx.resume().then(() => {
    console.log('Playback resumed successfully');
  });
});
		var audioCtx = new (window.AudioContext || window.webkitAudioContext)(),
			oscillatorNode = audioCtx.createOscillator(),
			gainNode = audioCtx.createGain();

		var options = {
			mute: true,
			frequency: 500,
			direction: '',
			volume: 0.8,
			speed: 1,
			grossTune: 5,
			mediumTune: 0.5,
			fineTune: 0.05
		};

		var keys = [
			{
				key: 'q',
				direction: 'down',
				tune: options.grossTune
			},
			{
				key: 'w',
				direction: 'up',
				tune: options.grossTune
			},
			{
				key: 'e',
				direction: 'down',
				tune: options.mediumTune
			},
			{
				key: 'r',
				direction: 'up',
				tune: options.mediumTune
			},
			{
				key: 't',
				direction: 'down',
				tune: options.fineTune
			},
			{
				key: 'y',
				direction: 'up',
				tune: options.fineTune
			}
		];

		var frequencyElement = document.getElementById('frequency');

		oscillatorNode.connect(gainNode);
		gainNode.connect(audioCtx.destination);
		oscillatorNode.start();
		oscillatorNode.frequency.value = options.frequency;
		gainNode.gain.value = 0;

		function setDirectionFrequency() {
			if (options.direction === 'up'){
				options.frequency += options.speed;
			}
			if (options.direction === 'down'){
				options.frequency -= options.speed;
			}
			oscillatorNode.frequency.value = options.frequency;
			frequencyElement.innerHTML = options.frequency.toFixed(2);
		}

		function setNotes(event) {
			if (event.key === '') { // space bar
				if (options.mute) {
					gainNode.gain.value = options.volume;
					options.mute = false;
				} else {
					gainNode.gain.value = 0;
					options.mute = true;
				}
			}
			for (var i = keys.length - 1; i >= 0; i--) {
				if(event.key === keys[i].key) {
					options.direction = keys[i].direction;
					options.speed = keys[i].tune;
				}
			}
		}

		function resetDirection() {
			options.direction = '';
		}

		function keyDown(event) {
			if (event.target.localName === 'button') {
				setNotes({key: event.target.value});
			}

		}

		setInterval(setDirectionFrequency, 40);

		var element = document.querySelector('#controls');

		element.addEventListener('touchstart', keyDown);
		element.addEventListener('touchend', resetDirection);
		element.addEventListener('mousedown', keyDown);
		element.addEventListener('mouseup', resetDirection);

		document.addEventListener('keydown', setNotes);
		document.addEventListener('keyup', resetDirection);
</script>
</body>
</html>

@farooqkz
Copy link

farooqkz commented Nov 6, 2021

Suggestion: Use a repo with Github pages instead of Gist so that people could see the online version and try it very easily. Also much easier to contribute with PR.

@zhanglongqi
Copy link

Added the start button to account for the "If you create your AudioContext on page load, you’ll have to call resume()" issue from https://developers.google.com/web/updates/2017/09/autoplay-policy-changes#webaudio

<!DOCTYPE html>
<html>
<meta name="viewport" content="width=device-width, user-scalable=no">
<head><title>SOUND</title></head>
<body>
<style>
	button{
		height:100px;
		width:100px;
	}

	body {
		font-family:Verdana, Geneva, sans-serif;
		font-size:medium;
		text-align: center;
	}
</style>
<div id="controls">
	<p>
		<button class="start">Start</button>
	</p>
	<h1>Frequency: <span id="frequency"></span></h1>
	<p>	Gross
		<div>
			<button value="q">Q</button>
			<button value="w">W</button>
		</div>
	</p>
	<p>	Medium
		<div>
			<button value="e">E</button>
			<button value="r">R</button>
		</div>
	</p>
	<p> Fine
		<div>
			<button value="t">T</button>
			<button value="y">Y</button>
		</div>
	</p>
	<p>
		<button value="">Play/Pause</button>
	</p>

</div>
<script type="text/javascript">

// One-liner to resume playback when user interacted with the page.
document.querySelector('button.start').addEventListener('click', function() {
    audioCtx.resume().then(() => {
    console.log('Playback resumed successfully');
  });
});
		var audioCtx = new (window.AudioContext || window.webkitAudioContext)(),
			oscillatorNode = audioCtx.createOscillator(),
			gainNode = audioCtx.createGain();

		var options = {
			mute: true,
			frequency: 500,
			direction: '',
			volume: 0.8,
			speed: 1,
			grossTune: 5,
			mediumTune: 0.5,
			fineTune: 0.05
		};

		var keys = [
			{
				key: 'q',
				direction: 'down',
				tune: options.grossTune
			},
			{
				key: 'w',
				direction: 'up',
				tune: options.grossTune
			},
			{
				key: 'e',
				direction: 'down',
				tune: options.mediumTune
			},
			{
				key: 'r',
				direction: 'up',
				tune: options.mediumTune
			},
			{
				key: 't',
				direction: 'down',
				tune: options.fineTune
			},
			{
				key: 'y',
				direction: 'up',
				tune: options.fineTune
			}
		];

		var frequencyElement = document.getElementById('frequency');

		oscillatorNode.connect(gainNode);
		gainNode.connect(audioCtx.destination);
		oscillatorNode.start();
		oscillatorNode.frequency.value = options.frequency;
		gainNode.gain.value = 0;

		function setDirectionFrequency() {
			if (options.direction === 'up'){
				options.frequency += options.speed;
			}
			if (options.direction === 'down'){
				options.frequency -= options.speed;
			}
			oscillatorNode.frequency.value = options.frequency;
			frequencyElement.innerHTML = options.frequency.toFixed(2);
		}

		function setNotes(event) {
			if (event.key === '') { // space bar
				if (options.mute) {
					gainNode.gain.value = options.volume;
					options.mute = false;
				} else {
					gainNode.gain.value = 0;
					options.mute = true;
				}
			}
			for (var i = keys.length - 1; i >= 0; i--) {
				if(event.key === keys[i].key) {
					options.direction = keys[i].direction;
					options.speed = keys[i].tune;
				}
			}
		}

		function resetDirection() {
			options.direction = '';
		}

		function keyDown(event) {
			if (event.target.localName === 'button') {
				setNotes({key: event.target.value});
			}

		}

		setInterval(setDirectionFrequency, 40);

		var element = document.querySelector('#controls');

		element.addEventListener('touchstart', keyDown);
		element.addEventListener('touchend', resetDirection);
		element.addEventListener('mousedown', keyDown);
		element.addEventListener('mouseup', resetDirection);

		document.addEventListener('keydown', setNotes);
		document.addEventListener('keyup', resetDirection);
</script>
</body>
</html>

this one works, and UI better

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment