Asynchronous Keyboard Input in JavaScript

In some applications, such as games, the ‘keydown’ and ‘keyup’ events are not enough, especially when working in asynchronous code. If you hold down a key, the ‘keydown’ event fires over and over on a rate specified by the client OS. If we’re using asynchronous code, however, we can specify a sort of Key State. This will be similar to the C++ function GetAsyncKeyState, or C#’s XNA Framework function Keyboard.GetState (except a little different, you’ll see).
How we go about this is to create an array and every time a key is pressed, push that key into the array, and if it is released, remove the key from the array. Here are the basic functions:

keysDown = [];
addKey = function(event) {
	var key = event.keyCode;
	if(keysDown.indexOf(key) == -1){ //If the key is not already in the array
		keysDown.push(key); // Add the key to the array
	}
}
removeKey = function(event) {
	var key = event.keyCode;
	keysDown.splice(keysDown.indexOf(key),1); // Don't confuse with the slice function, the splice function removes items starting at the first argument for a length of the second argument.
}
window.addEventListener('keydown',addKey);
window.addEventListener('keyup',removeKey);

Do note that the indexOf function is not supported in IE8 or previous versions, a thread on StackOverflow has a good recommendation as for a fix.

Now you might be thinking that this doesn’t give us a whole lot of functionality over the window.addEventListener function, but this is where the magic happens. The next bit of code needs to be added to the async function you have running. Here’s an example used in a game loop. The goal of the code is to check if the up arrow is pressed, and jump if it is:

var currentInput = [],
	previousInput = [];
function loop() {
	// Get the current array of keys pressed
	currentInput = keysDown.slice(0);
	//This should be a different function, but I'll keep it here for readability
	for (var i=0; i<currentInput.length; i++) {
		switch (currentInput[i]) {
			case 38: // Up Arrow
				if(previousInput.indexOf(38) == -1){ //Previous input Does NOT contain 38
					//Jump
				}
				break;
			case 39:
				// walk right
			default:
				break;
		}
	}
	// Make sure we know what key was pressed last
	previousInput = currentInput.slice(0);
}

Note: do not try to implement this without using the slice function! If I were to just set currentInput = keysDown; (as I would in many other languages) it would make currentInput a reference to keysDown, not a duplicate. More info here.

See an example of Async Keyboard Input in Action here

Leave A Comment

You must be logged in to post a comment.