Skip to content

liftoff/HumanInput

Repository files navigation

HumanInput - Human-Generated Event Handling for Humans

HumanInput is a tiny (~8.1kb gzipped), high-performance ECMAScript (JavaScript) library for handling keyboard shortcuts and other human-generated events:

// Create a new instance with the element you want to watch for events
var HI = new HumanInput(window);
HI.on('ctrl-s', (event) => { HI.log.info('Keyboard events!') });
HI.on('click:.someclass', (event) => { HI.log.info("Mouse events!") });
HI.on('ctrl-a n', () => { HI.log.info("Sequences!") });
HI.on('⌘-ç', (event) => {
    HI.log.info('Works with non-US keyboard layouts (and ⌘⌥⇧)!')});
HI.on('paste', (event, data) => {
    HI.log.info('Clipboard and more! User pasted:', data)});
HI.on('speech"This is a test"', (event, transcript) => {
    HI.log.info('Speech recognition!')});
HI.on('gpad:button:4:down', (event, buttonVal, gamepadObj) => {
    HI.log.info('Gamepad support!')});
HI.on('applause', () => {
    HI.log.info('<bows> No seriously: It has clap detection!')});
Author

Dan McDougall

Contribute

Provide Financial Assistance to HumanInput

A Cloud-High Overview

The above is but a tiny fraction of what's possible with HumanInput. The library has support for:

  • Keyboard events (including key location/state/event specificity and non-US keyboard layouts!):

    HI.on('keydown:shiftleft', doLeftPaddle)
  • Any-event-as-a-modifier (aka combo events):

    HI.on('a-w', doUpLeft)
  • Mouse/Touch/Gesture and Multitouch events:

    HI.on('shift-click', doShiftClick);
    HI.on('pointer:left:down', shoot);
    HI.on('pan', doPan);
    HI.on('multitouch:2:tap', twoFingerTap);
    HI.on('multitouch:3:pan', threeFingerPan);
  • Clipboard and selection events:

    HI.on('select:"select this text"', userFollowsDirections)
  • Event sequences:

    HI.on('up up down down left right left right b a enter', doKonamiCode)
  • On-demand, real-time event/state tracking:

    HI.isDown('shift-a') == true
  • Document visibility events:

    HI.on('document:visible', doWelcomeBack)
  • Device orientation events:

    HI.on('portrait', doPortrait)
  • Bind whatever context you want to events so this is what you want it to be:

    HI.on('ctrl-a n', nextScreenFunc, screenObj)
  • Specify how many times an event callback can be called:

    HI.once('enter', doSubmit);
    HI.on('faceplant', wakeUp, someContext, 5);
  • A powerful filtering mechanism to ensure that events only get triggered when you want them to:

    HI.filter = myFilterFunc
  • Events support 'scopes' which you define and enable/disable at-will:

    HI.on('controlpanel:ctrl-h', doControlHelp);
    HI.pushScope('controlpanel');
    // Stuff gets done
    HI.popScope('controlpanel');
  • If the (browser-fired) event has a 'target' attribute you can use the element ID or a class to handle events for specific elements (e.g. if you've instantiated HumanInput on the window):

    HI.on(['click:#someelement', 'contextmenu:.someclass'], doStuff);
    // NOTE: This is super efficient use of event listeners!
  • Pause and resume handling of events on-the-fly:

    HI.pause(); HI.resume();
  • Optional plugin: Clap detection events:

    HI.on('doubleclap', clapOnClapOff)
  • Optional plugin: Gamepad events (with high performance state checking to integrate with game loops!):

    HI.on('gpad:button:4:down', doJump)
  • Optional plugin: Idle (inactivity) events (super low overhead!):

    HI.on('idle', function(lastActivity) {
        console.log('Idle: User was last active at:', lastActivity);
    });
  • Optional plugin: Speech recognition events (literally yell at your machine and it could take it personally!):

    HI.on('speech:"why are you blinking"', explain);
    HI.on('speech:"open the pod bay doors"', sorryDave);
  • Up to you: It's a great general-purpose event lib:

    HI.on('custom:event', handleMyEvent);
    HI.trigger('custom:event', someValue);
  • Up to you: It's also got a nice logger:

    > var myLogger = new HI.logger('INFO', '[myapp]');
    > myLogger.info("Tool cool!");
    [myapp] Too Cool!

HumanInput has no external dependencies and was made with only the finest vanilla JavaScript extract!

Note

For the sake of brevity let's just assume that we've already called var HI = new HumanInput(window) in the rest of the documentation (unless otherwise noted).

Browser Compatibility

Chrome Firefox IE Opera Safari
Yes Yes Yes Yes Yes!

Really, every little bit of HumanInput should work in all the major browsers running on Linux, Macs, and even old fashioned Windows desktops! Go nuts!

Plugins on the other hand...

Plugin Browser Compatibility

Speech Recognition Plugin

The Speech Recognition plugin requires the Speech Recognition API which is supported in Chrome and Firefox (requires enabling a flag) as of 6/16/2016.

Gamepad Plugin

The Gamepad plugin relies on the Gamepad API which is supported in Chrome, Firefox and Opera as of 6/16/2016.

Clapper Plugin

The Clapper plugin requires the Audio API which is supported in basically everything except IE as of 6/16/2016.

Live Demos

Debugging (set the logLevel)

Before learning anything else about HumanInput you should learn how to debug events! The 'key' (haha) is to set the logging level to "DEBUG":

var settings = {logLevel: "DEBUG"};
// Note: The logLevel is not actually case sensitive I just like shouting DEBUG
var HI = new HumanInput(window, settings); // Give settings when instantiating

Then whenever HumanInput triggers an event you'll see all the details about it in your browser's JavaScript console like: [HI] triggering: click [MouseEvent]. Warning: It can be wicked verbose (but it's worth it).

Alternatively, you can modify the logLevel on-the-fly with: HI.log.setLevel("DEBUG")

Events

HumanInput is an event library at its core and it classifies events into these categories:

  • Single: HI.on('a', doSomething)
  • Combo: HI.on('meta-a', doSomething)
  • Ordered Combo: HI.on('a->s->d', doASD)
  • Sequence: HI.on('up up down down left right left right b a enter', konamiCode)
  • Hold: HI.on('hold:750:pointer:left', doLongPress')

Just about any kind of event can be mixed and matched with any other kind of event. For example, you could use shift-click which combines keyboard and mouse events. You can take it a step further and mix such things into sequences like a-click dblclick f. Here's a ridiculous example to demonstrate THE POWER of HumanInput:

HI.on('gpad:button:2->shiftleft speech:"testing"',
    doTestSpeechIfGpadButton2withLeftShiftwasPressedBeforehand)``

Yeah, that actually works (if you have the gamepad and speech plugins and enabled).

Note

Except for ordered combos and sequences the order in which you define your combo event doesn't matter! ctrl-shift-a works just the same as shift-ctrl-a or even a-shift-ctrl (all events get sorted into a specific order before registration; expect the debug output to represent that ordering as such).

There's three event methods:

  • on(event, someFunction, context, times): When event is triggered call someFunction with context bound to this n times.
  • off(event, someFunction, context): Remove the matching event/someFunction/context combination. If only the event is given all matching functions/contexts will be removed. If no context is given all matching event/function combinations will be removed. Calling off() with no arguments will remove all events.
  • trigger(event, [arguments]: Trigger the event passing it arguments (as many as you want).

You can also use the convenient once() shortcut for events you only want to fire one time. Equivalent to: on(event, someFunc, context, 1).

Sequence Events

Not all event types can be used with sequences. For example, 'click' and 'dblclick' events are not added to the sequence buffer since they'd be redundant with 'pointer:left'. Here's a handy table of all the events that can end up in the sequence buffer and what they'll show up as:

Input Type Sequence Events
Mouse/Touch/Pointer pointer:left, pointer:middle, pointer:right
Wheel wheel:up, wheel:down, wheel:left, wheel:right, wheel:in, wheel:out
Keyboard Individual keys: a, tab, space, etc
Combos shift-pointer:left, ctrl-shift-f, etc
Gamepad gpad:button:1, gpad:button:2, etc
Speech speech:"what was spoken" (the final recognition, not speech:rt: events)
Claps clap, doubleclap, applause
Button/Key States with Sequences

Events that have ':down' and ':up' states get added to the sequence buffer when buttons and keys are released (i.e. when they change from ':down' to ':up'). Not when they're pressed.

Filtering

If you want to prevent certain events from being added to the sequence buffer see the Filtering section.

Binding Multiple Events at Once

You can bind multiple events to a single function by passing them as an array: HI.on(['a', 'b'], doAorBStuff)

Event.preventDefault()

If the event type supports it you can make sure that Event.preventDefault() gets called by simply having your event function return false:

var preventBookmarking = function(event, key, code) {
    HI.log.info("No bookmarking!");
    return false; // Will ensure event.preventDefault() gets called
};
HI.on('ctrl-b', preventBookmarking);

Or you could just, "call it your damned self" since the browser-generated event is passed to the triggered function as the first argument :)

Event Aliases

HumanInput includes a number of convenient event aliases which you can use to save some typing:

// Copied right out of humaninput.js
self.aliases = {
    tap: 'click',
    taphold: 'hold:750:pointer:left',
    clickhold: 'hold:750:pointer:left',
    middleclick: 'pointer:middle',
    rightclick: 'pointer:right',
    doubleclick: 'dblclick', // For consistency with naming
    konami: 'up up down down left right left right b a enter',
    portrait: 'window:orientation:portrait',
    landscape: 'window:orientation:landscape',
    hulksmash: 'faceplant',
    twofingertap: 'multitouch:2:tap',
    threefingertap: 'multitouch:3:tap',
    fourfingertap: 'multitouch:4:tap'
};

You can add your own aliases as well:

HI.aliases.invoke = 'ctrl-a';
HI.aliases['★'] = 'ctrl-b';
HI.on('invoke n', newWindow);
HI.on('★', newBookmark);
Note

You can use emit() instead of trigger() if you're triggering events yourself (one is an alias to the other).

Hold Events

Hold events can be used to determine when a user has held (down) a button, key, or other type of event for a specific length of time (in milliseconds). Here's an example of an event that will be triggered after the user holds down the left mouse button (or their finger on a touchscreen) for 1.5 seconds:

HI.on('hold:1500:pointer:left', function(event, elapsed) {
    HI.log.info("User touched:", event.target, " held down for: ", elapsed);
});

There's three settings that control 'hold' events:

  • holdInterval (number) [250]: How often to issue 'hold' events (controls the setTimeout() function that repeatedly calls these events).
  • moveThreshold (number) [5]: How many pixels the mouse/pointer/finger can move before a 'hold' event is cancelled. Only applies to pointer/mouse/touch events.
  • listenEvents: 'hold' (string) [present]: If 'hold' is present in the 'listenEvents' setting HumanInput will trigger 'hold' events. If not present it will not trigger this event type. Hold events are enabled by default.

Remapping/Renaming Events

HumanInput lets you re-map (aka rename) any event you wish via the map() function or via the eventMap setting:

var myMap = {'w': 'moveup', 'a': 'moveleft', 's': 'movedown', 'd': 'moveright'};
// Apply an eventMap at instantiation:
var HI = new HumanInput(window, {eventMap: myMap});
// Apply new eventMap mappings dynamically:
HI.map({'space': 'jump'});
HI.on('moveup', function(e) { HI.log.info('moveup'); });
// Pretend the user pressed the 'w' key; here's what you'd see in the console:
[HI] moveup

This feature also works with the isDown() function: HI.isDown('moveup') == true.

Note

If HI.init() is called any eventMap changes that were applied via HI.map() will be lost.

Handling Child Events (You Don't Need Multiple Instances of HumanInput)

Say you've instantiated HumanInput on the window (var HI = new HumanInput(window)) and you want to call a function whenever a user clicks a particular button on the page. Instead of creating a new instance of HumanInput for that particular button you can do this:

var HI = new HumanInput(window), // NOTE: 'window' is important here
    myButton = document.querySelector('#mybutton');
HI.on('click', function(event) {
    var whatWasClicked = e.target; // This is the element that the user clicked
    if (whatWasClicked === myButton) {
        HI.log.info("My button was clicked!");
    }
});

What about handling events for all elements matching say, a particular class? Here's how:

var HI = new HumanInput(window), // NOTE: 'window' is important here
    classToMatch = 'someclass';
HI.on('click', function(event) {
    var whatWasClicked = e.target;
    if (whatWasClicked.classList.contains(classToMatch)) {
        HI.log.info("An element with class: " + classToMatch + " was clicked!");
    }
});

Having a single instance of HumanInput on the window is extremely efficient since it only requires one set of event listeners (from addEventListener()) to handle all child events on the page.

Now that you understand how to handle bubbling-up events in a manual fashion here's a trick/shortcut:

var HI = new HumanInput(window); // NOTE: Same as above; use 'window'
HI.on('click:#someelement', function(event) {
    HI.log.info("#someelement was clicked!", event);
});

Yeah, yeah: Why wasn't this mentioned previously? Because this is documentation; not a quickstart! You can use '#' to indicate a specific element id or '.' to indicate a particular class...

HI.on('pointer:down:.someclass', function(event) {
    HI.log.info("An element with .someclass was clicked!", event);
});
Note

This feature only works for singluar classes (you can't do '.someclass.someotherclass'). If you need more specificity, well, you know how to examine the event yourself because you read the previous section!

Note #2

The '#' and '.' syntax for specifying elements doesn't work with sequences (though it does work with combos and ordered combos!).

To obtain teeny tiny performance boost and take a huge chunk out of debugging spam you can pass disableSelectors = true as a setting when instantiating HumanInput.

listenEvents

HumanInput will add event listeners to the given element (first argument to HumanInput()) for all the (browser) events given via the listenEvents setting. So if you wanted HumanInput to only listen for mouse events you could do something like this:

var settings = {listenEvents: ['mousedown', 'mouseup']};
// Provide the settings when instantiating:
var HI = new HumanInput(window, settings);
Note

You can reference the active listenEvents at any time via: HI.settings.listenEvents

The default listenEvents (which can vary depending on plugins) can be found via the HumanInput.defaultListenEvents property:

> console.log(HumanInput.defaultListenEvents);
["keydown", "keypress", "keyup", "click", "dblclick", "wheel", "contextmenu",
"compositionstart", "compositionupdate", "compositionend", "cut", "copy",
"paste", "select", "scroll", "pointerdown", "pointerup"]

If you have the '-full' version of HumanInput "speech" and "clapper" will be present in defaultListenEvents.

If you wish to add an event to the defaults (instead of completely overriding them all at once) you can use the addEvents setting:

// Leave defaults alone but add 'gamepad'
var settings = {addEvents: ['gamepad']};
var HI = new HumanInput(window, settings);

If you wish to remove an event from the defaults (opposite of above) you can use the removeEvents setting:

// Leave defaults alone but remove 'hold':
var settings = {removeEvents: ['hold']};
var HI = new HumanInput(window, settings);
Note about events without built-in handlers (i.e. events unknown to HumanInput)

If you use an event name that doesn't have a corresponding HI._<eventname>() (note the underscore) function HumanInput will use HI._genericEvent() to add an associated event listener via addEventListener(). The idea being to future-proof HumanInput: Browser makers added a new 'foo' event? No problem... HumanInput will trigger('foo', theFooEvent) if you add it to 'listenEvents'! This will work even though nothing specific has been added to HumanInput to handle it yet.

Note about simulated events

Some listenEvents may be 'simulated events' that are emitted by different mechanisms. For example, there's no way to listen for gamepad events via addEventListener() so the gamepad plugin uses its own event loop to detect and emit 'gamepad' events (which are aliased to 'gpad' to save some typing). To get the details about that see the Gamepad Plugin section.

Filtering

Before triggering an event HumanInput will execute HumanInput.filter(). If the filter function returns true the event will be triggered as normal. If it returns false the event will not be triggered. The default HumanInput.filter() only applies to keyboard events and will return false if a textarea, input, or select element has focus.

To disable filtering just set HumanInput.filter() to a function that returns true:

// Disable the filter function
HI.filter = function(e) { return true };

Sequences (e.g. 'a b c') can be filtered via a similar mechanism:

// Don't allow mouse/touch/pointer or 'wheel' events into the sequence buffer
HI.sequenceFilter = function(e) {
    var disallowed = ['wheel', 'pointerup', 'mouseup', 'touchend'];
    if (disallowed.indexOf(e.type) === -1) { return true; }
};
Note

The 'pointerup' event type will eventually cover all mouse, touch, and pointer click-style (e.g. pointer:left) events.

State Tracking

You can check the state of most events (keys, mouse, buttons) in real-time using the HumanInput.isDown() function:

HI.isDown('a') == true;
HI.isDown('shift-a') == true; // Works with combos too
HI.isDown('pointer:left') == true; // ...and pointer/mouse/touch events!
Note

For reasons that should be obvious you can't use isDown() with key sequences (just events and event combos).

High-performance state tracking

The HI.isDown() function is very fast but it does have some overhead. If you want to maximize performince (say, inside a game loop) you can check the 'down' state of any key by examining the HI.state.down array:

// Hardcore state tracking; without a (non-native) function call
HI.state.down.indexOf('a') != -1; // The 'a' key is down

Just note that HI.state.down tracks the state of keys via KeyboardEvent.key and maintains the case it was given. This means that if the user presses the 'a' key it will be tracked as a lowercase 'a'. However, if the user is also holding down the 'ShiftLeft' key HI.state.down will hold an uppercase 'A' since that's what KeyboardEvent.key will give us. Also keep in mind that modifiers that have left and right equivalents will be stored in HI.state.down as such (e.g. 'ShiftLeft', 'ControlRight', etc).

Recording Events or Capturing a Keystroke

HumanInput provides two functions, startRecording() and stopRecording() that can be used to temporarily capture events triggered by the user. This can be useful when providing users with the ability to create/customize keyboard shortcuts. There's two (usual) ways to use these functions...

Record All Events

The first and simplest way: Obtain all or a subset of events that triggered since startRecording() was called:

HI.startRecording();
// Let's pretend we just want 'keyup:<key>' events...
var keyupEvents = HI.stopRecording('keyup:')
// You can safely call stopRecording() multiple times after startRecording():
var allEvents = HI.stopRecording(); // Returns all events (no filter)
Capture a Keystroke

If you just want to capture a single keystroke you can pass 'keystroke' as the argument to stopRecording() like so:

HI.startRecording();
HI.once('keyup', (e) => {
    var keystroke = HI.stopRecording('keystroke');
    HI.log.info('User typed:', keystroke, e);
});

Keyboard Support

It's probably easiest if we just provide examples of all the ways you can use keyboard events in HumanInput...

// Basic: Call a function when a specific key is pressed
HI.on('a', aKeyPressed); // Implied keyup:a
// Be more specific about the same thing
HI.on('keyup:a', aKeyReleased); // keydown works too (only losers use keypress)
// Call your function whenever *any* key is pressed
HI.on('keydown', theAnyKeyHasBeenFound);
// Keys typed with shift are handled automatically
HI.on('A', capitalAPressed); // Non-letters like '!' are also handled automatically!
// You can also specify a key's location if the browser knows the difference
HI.on('keydown:shiftleft', leftPaddle);
// Combos!  NOTE: Technically, *event* combos (not limited to keys!)
HI.on('ctrl-g', function(event) { HI.log.info('You pressed Control-g!'); });
// Bind a couple of key combos to the same function
HI.on(['ctrl-a', 'ctrl-shift-a'], someFunction); // ctrl-a *or* ctrl-shift-a call someFunction()
// Call a function when a certain sequence of keys is pressed
HI.on('ctrl-a n', nextVirtualWindow); // User types "ctrl-a" proceeded by "n"
// Now let's get *really* precise; call a function when the user presses
//   f, d, and s (in that specific order)
HI.on('f->d->s', doFDSCombo); // It's a key combo but with a specific order->of->events
// Same thing but the opposite order
HI.on('s->d->f', doSDFCombo);
// Note that the above also demonstrates how any key (or event!) can be a modifier
Note about shifted keys like 'A' or '!'

Because the shift key produces different characters depending on the keyboard layout you must be careful when binding events with HI.on(). If your intent is for the user to type shift-<somekey> to trigger an event then you should bind it that way instead of assuming ! is produced via shift-1. You don't need to worry about such things for capitalized characters though as they are always produced via shift-<key> regardless of the layout.

Keyboard events are triggered with KeyboardEvent, KeyboardEvent.key (normalized by HumanInput if warranted) and KeyboardEvent.code as arguments. So if you listen to just 'keydown' or 'keyup' you can examine the key that was pressed like so:

var whatKey = function(event, key, code) {
    HI.log.info(key, ' was pressed.  Here is the code:', code);
};
HI.on('keyup', whatKey);
Space: You. Are. The Only Exception

The spacebar is special in HumanInput because sequences are identified and separated by spaces (e.g. HI.on('a b c')) so if you want to bind the space key you have to use space (e.g. HI.on('alt-space')).

Textual Input Elements

As mentioned earlier in this document, by default HumanInput will not trigger keyboard events when the user has focused on a textarea, input, or select element. This is controlled via HumanInput.filter(). To change this behavior just override that function or set it to an empty function that always returns true: HI.filter = (e) => { return true }

Intelligent Key Repeat

By default HumanInput won't repeatedly trigger keyboard events for keys which are held down (aka "key repeat"). You can override this functionality by passing noKeyRepeat = false when instantiating HumanInput:

var settings = {noKeyRepeat: false}; // Trigger events constantly while keys are held
var HI = new HumanInput(window, settings);
HI.on('space', fireLasers);

Internationalization

HumanInput tries to be smart about international (non-US) keyboard layouts. If you type 'ç' using a Brazilian layout you should be able to attach an event to that key like so: HI.on('ç', doStuff). Note that this capability is largely dependent on browser support and it doesn't usually work with the Control key (ctrl) for legacy reasons. As of writing this documentation the only major browser lacking support for international keyboard layouts (in this way) is Safari (Apple needs to get with the KeyboardEvent.key program!). It should work great with Chrome/Chromium, Firefox, Opera, and even IE.

Key Aliases

If you want to be freaky deaky (or extreme in your minification) you can use unicode symbols for their respective keys:

HI.on('⇧-b', shiftBPressed); // Same as: 'shift'
HI.on('⌥-c', optionCPressed); // Same as: 'alt', 'option'
HI.on('⌘-c', commandCPressed); // Same as: 'os', 'meta', 'win' 'command', 'cmd'
Note

You can also use control instead of ctrl but who wants to type all those extra characters? :)

Unique Numpad

Say you want to differentiate between '/' and the same key on the numpad. You can do that but you must set uniqueNumpad = true when instantiating HumanInput like so:

var settings = {uniqueNumpad: true};
var HI = new HumanInput(window, settings);

Then when you want to attach an event to a numpad key just prefix it with numpad like so:

HI.on('numpad*', numpadStarFunc);
HI.on('numpad/', numpadSlashFunc);
HI.on('numpad5', numpadFiveFunc);

Composition (IME) Support

Composition and Input Method Entry (IME) support is fairly straightforward:

HI.on('composing:"Tes"', examineInput); // User just added 's' after 'Te'
HI.on('composed:"Test"', compositionUpdated); // User completed their composition
// You can do this too if you want to handle things yourself:
HI.on('compositionend', compositionEndedFunc); // Handle the event however you like

Faceplant Support

A very important feature in any JS lib that handles keyboard events: Detecting when a face slams into the keyboard...

HI.on('faceplant', wakeUpFool); // How could any keyboard lib not have this? :D

Try it!

Note

hulksmash also works ᕙ(⇀‸↼‶)ᕗ

Mouse, Touch, Multitouch, and Pointer Event Support

HumanInput supports mouse, touch, and pointer events and includes a bunch of handy dandy shortcuts to deal with it all...

Note

Use 'pointer' when you want to cover mouse and touch events at the same time.

// Basics:
HI.on('click', doClick);
HI.on('tap', doClickStuff); // Same exact thing as above ('tap' is an alias for 'click')
HI.on('pointer:down', doMouseDownStuff); // Same as 'mousedown' or 'touchstart'
// Be more specific
HI.on('pointer:right:down', doRightByMe);
HI.on('middleclick', doPaste); // Alias to 'pointer:middle'
// Be *very* specific
HI.on('mouse:7:up', handleMouseSeven); // Only fire for mouse clicks using button 7; no touches!
// Combine with keys (or other events) as modifiers!
HI.on('ctrl-click', doCtrlClick);
// Mouse sequence support
HI.on('dblclick click', handleTripleClick); // Triple-click
HI.on('dblclick a-s-d-f', homeRowMasher); // Use your imagination!
// Pan support
HI.on('pan:.panclass', panAround);
// Basic gesture support
HI.on('swipe:up', swipeUp);
HI.on('swipe:right', swipeRight);
// Multitouch (multi-*pointer*) support
HI.on('multitouch:2:tap', twoFingerTap);
HI.on('multitouch:3:pan', threeFingerPan);
Note

HumanInput does not call addEventListener() for mouse or touch events if pointer events can be used (it uses browser feature detection).

Multitouch gestures work with sequences

Makes for some fun sequences: pointer:left multitouch:2:tap multitouch:3:tap multitouch:4:tap

Effective Pan Handling

Location, location, location! Just kidding. Not that kind of panhandling!

Pan events need a bit of explanation in order to use them to effectively: HumanInput doesn't manipulate the DOM--that's your job! (because everyone/every framework does it differently) Having said that, implementing a 'pan' feature is quite trivial with HumanInput but there is one thing you must do for it to work properly: return false (or call preventDefault()) in your 'pan' handler. Example:

// xPan and yPan represent the current state (so we don't snap back every time the user pans)
var xPan = 0, yPan = 0;
HI.on('pan:#elemtopan', function(e, panObj) {
// The element we want to pan is the event target (pretty much always):
    var panElem = e.target;
// The 2nd arg passed to 'pan' events include a convenient object (panObj):
    xPan += panObj.xMoved; // xMoved and yMoved represent the number of pixels
    yPan += panObj.yMoved; // that the pointer has moved since the pan started
// Now we can "Move it! Move it!"
    panElem.style.transform = 'translate3d('+xPan+'px,'+yPan+'px,0)';
    return false; // <-- IMPORTANT!
    // Alternatively you could just do this:
    // e.preventDefault()
});

The reason you need to ensure preventDefault() gets called is so that the browser doesn't try to scroll or highlight text while your pan operation is in motion. In fact, that's all a 'pan' event is: A mousemove, touchmove, or pointermove event handler that gets added after mousedown/touchstart/pointerdown. So by calling preventDefault() on 'pan' you're essentially calling it for the mousemove (and equivalents) event.

Pan events enabled by default

Pan events are enabled by default but can be disabled by removing 'pan' from the 'listenEvents' setting.

If anyone wants to assist, the following multitouch event types are in the TODO list (not yet implemented):

HI.on('multitouch:2:swipe:right', swipeRight); // Multi-finger swipes
HI.on('pinch', zoomOut); // Pinch-to-zoom; patently obvious!
HI.on('spread', zoom); // Opposite of pinch
HI.on('rotate', rotate); // Two-finger rotation

Mousewheel and Scroll Event Support

Taking advantage of mousewheel and scrolling events is very straightforward:

HI.on('wheel', wheelMoved);        // Wheel moved (unspecified)
HI.on('wheel:up', wheelUp);        // Wheel scrolled up
HI.on('wheel:down', wheelDown);    // Wheel scrolled down
HI.on('wheel:left', wheelLeft);    // Wheel scrolled left
HI.on('wheel:right', wheelRight);  // Wheel scrolled right
HI.on('scroll', scrolled);         // User scrolled (unspecified)
HI.on('scroll:up', scrollUp);      // User scrolled up
HI.on('scroll:down', scrollDown);  // User scrolled down
HI.on('scroll:left', scrollLeft);  // User scrolled left
HI.on('scroll:right', scrollRight);// User scrolled right
Note

Most browsers implement a shift-scroll keyboard shortcut to scroll left and right. To ensure the most compatibility HumanInput will fire both the regular wheel event (e.g. wheel:right) in addition to a combo event (e.g. shift-wheel:right) if the shift key is held while scrolling left or right.

What's the difference between 'wheel' and 'scroll' events?

The wheel events refer to a physical device whereas scroll events can be triggered by many things such as the user pressing the spacebar, down arrow, or clicking and dragging the scrollbar with their mouse.

Scroll Events

When scroll events are triggered they are passed the scroll event (from the browser) and the number of pixels scrolled. In the case of ambiguous 'scroll' events the triggered callback will be called with an object containing a 'x' and 'y' value. Example:

HI.on('scroll', function(e, scrollObj) {
    HI.log.info('User scrolled X:', scrollObj.x, ' Y:', scrollObj.y);
});
All scroll events are de-bounced

50ms to be precise. This is to prevent zillions of tiny pixel scroll events from firing constantly while the user is scrolling. Don't worry, the scroll distances will still be accurate.

Note

The 'x' and 'y' numbers can be negative with ambiguous 'scroll' events.

The directional scroll events such as 'scroll:down' will just be passed the pixel value as a number:

HI.on('scroll:down', function(e, distance) {
    HI.log.info('User scrolled down ', distance, ' pixels');
});

Passive Scrolling Support

If you undestand the implications you can set {passive: true} for 'touchstart' events via eventOptions['touchstart'] when instantiating HumanInput:

// Can be a significant performance boost when scrolling on touch-enabled devices:
var settings = {eventOptions: {touchstart: {passive: true, capture: true}}};
var HI = HumanInput(window, settings);

Just be aware that this will make it so that preventDefault() does nothing for that particular event when it is triggered by HumanInput. For more information see the standard (search for 'passive' on that page).

Clipboard and Selection Support

HumanInput includes extensive support for clipboard and text selection events:

HI.on('paste', doStuffWithPaste);
HI.on('copy', seeWhatWasCopied);
HI.on('cut', seeWhatWasCut);
// ...and you can match what was pasted/copied/cut in the event itself!
HI.on('paste:"127.0.0.1"', remindUserAboutLocalhostBeingEasyToType);

Clipboard events are triggered with the ClipboardEvent.clipboardData as the second argument. So you can see what the user cut/copied/pasted like so:

var clipboardHandler = function(event, data) {
    console.log('event:', event, 'clipboard data:', data);
};
HI.on(['cut', 'copy', 'paste'], clipboardHandler);

Text selection events work in a similar fashion and fire when the user releases their mouse (or with each selected letter if the user is highlighting text with the keyboard):

HI.on('select', function(e, whatWasSelected) {
    console.log("User selected:", whatWasSelected});

You can also craft events that trigger when matching text is selected like so:

HI.on('select:"select this text"', userFollowsDirections);

Input Event Support

Input events are triggered with the event and "what was input" as the first and second argument, respectively (just like 'select' events):

HI.on('input', function(e, whatWasInput) {
    console.log("User input:", whatWasInput});

Just like selection and clipboard events, you can craft events that trigger when the user inputs something specific:

HI.on('input:"idkfa"', cheatMode);

Context Menu Support

Real simple:

HI.on('contextmenu', contextMenuFunc);
Note

This can be wicked useful when combined with scopes!

Window and Document Events

HumanInput supports tracking the state of the document and window via the following events:

HI.on('document:hidden', enableNinjaMode);   // NOTE: Always available
HI.on('document:visible', disableNinjaMode); // NOTE: Always available
HI.on('window:resize', windowWasResized); // See below about availability
HI.on('window:blur', windowNoLongerFocused);
HI.on('window:beforeunload', userNavigatingAway);
HI.on('window:hashchange', userClickedAnchor);
HI.on('window:languagechange', userChangedLang);
HI.on('window:orientation:landscape', doLandscapeView); // Alias: 'landscape'
HI.on('window:orientation:portrait', doPortraitView); // Alias: 'portrait'
HI.on('fullscreen', (isFullScreen) => {
// The function called by the 'fullscreen' event will be passed true/false:
    HI.log.info('fullscreen:', isFullScreen);
});
Note About 'window:' Events

The various 'window:' events are only triggered if HumanInput was instantiated with the window object as the first argument. 'document:hidden/visibile' events are always triggered since plugins depend on this event to pause and resume under certain circumstances. The above 'window' events are not controlled via the listenEvents setting.

Advanced Stuff

HumanInput Settings

Besides logLevel, listenEvents, eventMap, uniqueNumpad, and noKeyRepeat HumanInput takes the following settings:

  • addEvents (array) [[]]: An array of events you wish HumanInput to listen for via addEventListener() in addition to the defaultListenEvents. This setting is just a convenience; {addEvents: ['foo']} is a lot less to type (and easier to read) than {listenEvents: HumanInput.defaultListenEvents.concat(['my', 'extra', 'events'])}.
  • disableSequences (bool) [false]: Set to true if you want to disable sequence events like ctrl-a n. This can save a few CPU cycles and lessen debug output if you're not using that feature (would likely only matter for games).
  • disableSelectors (bool) [false]: Set to true if you want to disable the selector syntax functionality (e.g. on('<someevent>:#someelement')). This can also save a few CPU cycles (a lot less than 'disableSequences') but the main benefit is reducing debug output (when set to false).
  • eventOptions (object) [{}]: An object containing event names and their respective options that will be passed as the third argument when calling addEventListener(). Look here for more info about the options (3rd arg) you can pass to addEventListener().
  • maxSequenceBuf (number) [12]: The maximum length of event sequences.
  • sequenceTimeout (milliseconds) [3500]: How long to wait before we clear out the sequence buffer and start anew.
  • swipeThreshold (pixels) [50]: How many pixels a finger has to transverse in order for it to be considered a swipe.

Extra Events

  • After initialization HumanInput triggers the hi:initialized event.
  • After pausing HumanInput triggers the hi:paused event.
  • After resuming from a pause the hi:resume event will be triggered.

HumanInput.init() (aka Reset)

If you want to re-initialize/reset an instance of HumanInput you can call the instance's init() function and it will start anew, performing the following actions:

  1. The hi:reset event will be triggered. Note: Only triggered in an actual reset scenario; it doesn't do this when HumanInput is instantiated.
  2. All events, aliases, state tracking, keyMaps, and the scope will be set to defaults.
  3. All settings provided when you originally instantiated HumanInput will be re-applied.
  4. The hi:initialized event will be triggered.

Translation Functionality

HumanInput supports gettext-like translation of the few strings that it contains (e.g. informational debug and error messages) using a 'translate' function which can be provided via the settings argument when HumanInput is instantiated. Here's an overdone example:

var frenchTranslations = {
    'Resetting key states due to timeout': 'Réinitialisation etats clés en raison de timeout'
};
var myTranslateFunction = function(text) {
    // Return the text from frenchTranslations if available:
    return frenchTranslations[text] || text;
}
var settings = {logLevel: 'DEBUG', translate: myTranslateFunction},
    HI = new HumanInput(window, settings);
// User interacts with the page and eventually you see in the console:
[HI] Réinitialisation etats clés en raison de timeout

You can also change the translation function on-the-fly by swapping out l() like so:

HI.l = newTranslateFunc;

Tips & Tricks

Instantiate Using CSS Selector Syntax

You can instantiate HumanInput on a particular element using CSS selector syntax (internally it uses document.querySelector()):

var HI = new HumanInput('#someelement'); // It'll find it!

Reference the HumanInput Event Inside Callbacks via 'this'

Whenever an event gets triggered HumanInput attaches a HIEvent attribute to this when it calls associated callbacks:

HI.on('click:#someelement', function(event) {
    console.log("This is the event that triggered this function: " + this.HIEvent);
});
// Then when you click #someelement you'll see this in the console:
"This is the event that triggered this function: click:#someelement"
The One Exception

If you pass the 'window' (global) as the context (3rd arg) when calling HI.on() HumanInput will not attach 'HIEvent' to 'this' in order to prevent poisoning the global namespace.

Note About Arrow Functions

This feature won't work if your callback function is defined using arrow syntax (e.g. (e) => { <code here> }) because arrow functions don't work with .apply() which is what HumanInput uses to call event callbacks. It is an intentional limitation of arrow functions.

Custom Event Routing

The HIEvent feature can be wicked handy when used in conjunction with some slick programming patterns:

var events = ['cut', 'copy', 'paste']; // Events we want to handle
var routes = { // What functions to call for each event
    'cut': funciton(event, cutData) { HI.log.info('Do cut stuff'); },
    'copy': funciton(event, copiedData) { HI.log.info('Do copy stuff'); },
    'paste': funciton(event, pastedData) { HI.log.info('Do paste stuff'); },
};
var router = function() {
    // Call the function matching the event that was triggered
    var args = Array.apply(null, arguments);
    routes[this.HIEvent].apply(this, args);
}
HI.on(events, router);

Some readers will see this and think, "Well that's rather contrived! What's the point?" and others will think, "Oooooh! I'm so gonna use that! That is handy!"

Let Users Define Their Own Keyboard Shortcuts

If you combine the example above with the event remapping capability you can let your users define their own custom keyboard shortcuts for any and all functions in your application!

// Pretend these are the functions you want to assign to keyboard shortcuts:
var someFunc = function(e) { HI.log.info('Some function'); return false; };
var otherFunc = function(e) { HI.log.info('Other function'); return false; };
// Create a mapping of names-to-functions (this won't change):
var funcMap = {
    somefunc: someFunc,
    otherfunc: otherFunc,
    somefeature: HI.noop // Yet-to-be-assigned example
};
// Create an event map that maps events-to-names (the keys will change):
var eventMap = {
    'ctrl-i': 'somefunc', // Note: All lowercase
    'ctrl-m': 'otherfunc'
};
// Instantiate with your eventMap (or call map() with it later)
var HI = new HumanInput(window, {eventMap: eventMap});
var router = function() {
    // Call the function matching the event that was triggered
    var args = Array.apply(null, arguments);
    funcMap[this.HIEvent].apply(this, args);
}
// Assign our custom events (from funcMap) to call our router function:
HI.on(Object.keys(funcMap), router);

Explanation: In the above example, if the user types ctrl-f it will be automatically remapped (renamed) to somefunc when the event is triggered. Since our router() function is bound to the somefunc event it's what will get called by HumanInput. Then the router() function will call the respective function in our pretend application like so: funcMap[this.HIEvent].apply(this, args).

That's all fine and good but how do I use it to let my users assign their own keyboard shortcuts? Here's how:

// Use the recording feature!
// Pretend we have this awesome GUI API that creates dialog windows:
var closeDialog = GUI.dialog('Press the keystroke you wish to be assigned to someFunc');
HI.startRecording();
HI.once('keyup', function(e) {
    var keystroke = HI.stopRecording('keystroke');
    HI.log.info('User typed keystroke: ', keystroke);
    // Replace the key:value that calls someFunc with a new one
    for (var item in eventMap) {
        if (eventMap[item] == 'somefunc') {
            delete eventMap[item]; // Get rid of the old one
            eventMap[keystroke] = 'somefunc'; // Put in the new one
            break;
        }
    }
    HI.map(eventMap); // Update the eventMap in the current instance
    closeDialog(); // Close the dialog; you're done!
});
// The user can now use the new keystroke to call someFunc!

Presumably you'll serialize the eventMap to JSON and store it somewhere it gets restored when the user loads the page. Now your application supports customizable keyboard shortcuts like a native app!

HumanInput Plugins

Clapper Plugin

The Clapper plugin (which is automatically included in the '-full' version of humaninput.js) can detect clapping sounds like the old fashioned Clapper. Here's how to use it:

HI.on('clap', doClap);
HI.on('doubleclap', clapOnClapOff);
HI.on('applause', thankYouThankYou);

The Clapper plugin supports two settings:

  • clapThreshold (number) [120]: Relative amplitude microphone input needs to go over before a sound is considered a 'clap'.
  • autostartClapper (bool) [false]: Controls whether or not the plugin should start listening for clapping sounds immediately after instantiation.
  • autotoggleClapper (bool) [true]: Controls whether or not the plugin will automatically pause and resume itself when the page becomes hidden/unhidden.

You can tell the plugin to start listening for clap events by calling HI.startClapper() and stop listening by calling HI.stopClapper(). If the page becomes hidden the plugin will automatically stop listening for clap events and resume when the user returns to the page unless autotoggleClapper == false.

Note

There's a demo for speech recognition in the demo directory named, 'clapper'.

Gamepad Plugin

The HumanInput Gamepad plugin (which is automatically included in the '-full' version of humaninput.js) adds support for gamepads and joysticks allowing the use of the following event types:

Event Description Arguments
gpad:connected A gamepad was connected (<Gamepad object>)
gpad:disconnected A gamepad was connected (<Gamepad object>)
gpad:button:<n> State of button n changed (<Button Value>, <Gamepad object>)
gpad:button:<n>:down Button n was pressed (down) (<Button Value>, <Gamepad object>)
gpad:button:<n>:up Button n was released (up) (<Button Value>, <Gamepad object>)
gpad:button:<n>:value Button n value has changed (<Button Value>, <Gamepad object>)
gpad:axis:<n> Gamepad axis n changed (<Button axis value>, <Gamepad object>)

Detection Events

Whenever a new gamepad is detected or disconnected the gpad:connected and gpad:disconnected events will be triggered, respectively with the Gamepad object as the only argument.

Button Events

When triggered, gpad:button events are called like so:

HI.trigger(event, buttonValue, gamepadObj);

You can listen for button events using HumanInput.on() like so:

// Ensure 'gamepad' is included in listenEvents if not calling gamepadUpdate() in your own loop:
var settings = {addEvents: ['gamepad']};
var HI = new HumanInput(window, settings);
var shoot = function(buttonValue, gamepadObj) {
    HI.log.info('Fire! Button value:', buttonValue, 'Gamepad object:', gamepadObj);
};
HI.on('gpad:button:1:down', shoot); // Call shoot(buttonValue, gamepadObj) when gamepad button 1 is down
var stopShooting = function(buttonValue, gamepadObj) {
    HI.log.info('Cease fire! Button value:', buttonValue, 'Gamepad object:', gamepadObj);
};
HI.on('gpad:button:1:up', stopShooting); // Call stopShooting(buttonValue, gamepadObj) when gamepad button 1 is released (up)

For more detail with button events (e.g. you want fine-grained control with pressure-sensitive buttons) just neglect to add :down or :up to the event:

HI.on('gpad:button:6', shoot);
Note

The resulting buttonValue can be any value between 0 (up) and 1 (down). Pressure sensitive buttons (like L2 and R2 on a DualShock controller) will often have floating point values representing how far down the button is pressed such as 0.8762931823730469.

Button Combo Events

When multiple gamepad buttons are held down a button combo event will be fired like so:

HI.trigger("gpad:button:0-gpad:button:1", gamepadObj);

In the above example gamepad button 0 and button 1 were both held down simultaneously. This works with as many buttons as the gamepad supports and can be extremely useful for capturing diagonal movement on a dpad. For example, if you know that button 14 is left and button 13 is right you can use them to define diagonal movement like so:

HI.on("gpad:button:13-gpad:button:14", downLeft);

Events triggered in this way will be passed the Gamepad object as the only argument.

Note

Button combo events will always trigger before other button events.

Axis Events

When triggered, gpad:axis events are called like so:

HI.trigger(event, axisValue, GamepadObj);

You can listen for axis events using HumanInput.on() like so:

var moveBackAndForth = function(axisValue, gamepadObj) {
    if (axisValue < 0) {
        console.log('Moving forward at speed: ' + axisValue);
    } else if (axisValue > 0) {
        console.log('Moving backward at speed: ' + axisValue);
    }
};
HI.on('gpad:axis:1', moveBackAndForth);

Game and Application Loops

If your game or application has its own event loop that runs at least once every ~100ms or so then it may be beneficial to call HumanInput.gamepadUpdate inside your own loop instead of passing 'gamepad' via the 'listenEvents' (or 'addEvents') setting. Calling HumanInput.gamepadUpdate() is very low overhead (takes less than a millisecond) but HumanInput's default gamepad update loop is only once every 100ms. If you don't want to use your own loop but want HumanInput to update the gamepad events more rapidly you can reduce the 'gpadInterval' setting. Just note that if you set it too low it will increase CPU utilization which may have negative consequences for your application.

Note

The update interval timer will be disabled if the page is no longer visible (i.e. the user switched tabs). The interval timer will be restored when the page becomes visible again. This is handled via the Page Visibility API (visibilitychange event).

Gamepad State Tracking

The state of all buttons and axes on all connected gamepads/joysticks can be read at any time via the HumanInput.gamepads property:

var HI = HumanInput();
for (var i=0; i < HI.gamepads.length; i++) {
    console.log('Gamepad ' + i + ':', HI.gamepads[i]);
});
Note

The index position of a gamepad in the HumanInput.gamepads array will always match the Gamepad object's 'index' property.

Handling Multiple Gamepads

Since HumanInput 'gpad' events don't include the index of the gamepad device (for performance reasons) you'll need to distinguish between gamepads by looking at the 'index' property of the browser's Gamepad object (which will be passed as the second argument for all button/axis callbacks). Fortunately this is trivial as you can see:

HI.on('gpad:button:1:down', function(buttonVal, gamepadObj) {
    var gamepad = gamepadObj.index; // This is the differentiator
    // Pretend we're tracking which gamepad is which player inside playersObj:
    var player = playersObj[gamepad];
    // Do button 1 stuff for that player (the one using this gamepad)
});

Idle Plugin

The HumanInput Idle plugin (which is automatically included in the '-full' version of humaninput.js) regularly checks for user activity and triggers the 'idle' event if no activity is detected within a given 'idleTimeout' (default: 5m). When triggered, the 'idle' event will pass the Date() object representing the last period of activity as the only argument. Here's an example of how to use it:

HI.on('idle', function(lastActivity) {
    console.log('User is idle. They were last active at:', lastActivity);
});
Note About Efficiency

The Idle plugin is extremely efficient: It only checks for user activity every five seconds by default (controlled via 'idleCheckInterval') and does not waste loads of CPU with endles mousemove events (as is typical in the world of JavaScript idle checking functions/features). It uses 'click', 'keydown', 'scroll' and 'mousemove' events to detect user activity but the latter ('mousemove') is what only gets checked/added/removed every five seconds. In between those five seconds there won't actually be anything listening for the 'mousemove' event.

Idle Plugin Functions

You can start and stop the idle plugin checking for inactivity via the HI.startIdleChecker() and HI.stopIdleChecker() functions.

Idle Plugin Settings

  • autostartIdle (bool) [true]: Whether or not the idle checker will start automatically. Note: It only starts if 'idle' is in 'listenEvents' (and it's there by default).
  • idleTimeout (string) ['5m']: How long without activity before the 'idle' event will be triggered. Note: It takes human-readable strings to represent periods of time (see table below).
  • idleCheckInterval (number) ['5s']: How often should user activity be checked in milliseconds.

Time Strings

Character Meaning Example
(none) Milliseconds '500' -> 500 Milliseconds
s Seconds '60s' -> 60 Seconds
m Minutes '5m' -> 5 Minutes
h Hours '24h' -> 24 Hours
d Days '7d' -> 7 Days
M Months '2M' -> 2 Months
y Years '10y' -> 10 Years

Speech Recognition Plugin

The HumanInput Gamepad plugin (which is automatically included in the '-full' version of humaninput.js) adds support for triggering events based on speech recognition. It only works in Chrome at the moment but some day other browsers will support speech recognition too. Here's how to use it:

// Call a function when "This is a test" is recognized
HI.on('speech:"This is a test"', function(e) {
    HI.log.info("Recognized 'This is a test'");
});
// Call a function when "this is" is recognized as fast as possible
HI.on('speech:rt"This is a"', function(e) {
    HI.log.info("Recognized 'This is a test'");
});
// Call a function when *any* speech is recognized (do what you want with it)
HI.on('speech', function(e) {
    HI.log.info("Recognized:", transcript);
});
// Call a function when *any* speech is recognized in real-time
// (useful for detecting when it's processing)
HI.on('speech:rt', function(e) {
    HI.log.info("Recognized:", transcript);
});
Note

There's a demo for speech recognition in the demo directory named, 'dictate'.

What's the difference between speech and speech:rt? The 'speech:rt' form is fired more often and isn't as accurate. It's basically, "our best immediate guess as to what you said" whereas 'speech' is for the final, "after careful analysis this is what the computer thinks you said."

Language Selection

The speech recognition plugin attempts to detect your speaking language using the locale set in your browser. If it cannot be detected it will fall back to using "en_US". Alternatively, you can specify 'speechLang' as a setting when instantiating HumanInput like so:

var settings = {speechLang: "en_US"};
var HI = new HumanInput(window, settings);

Starting Speech Recognition (and autostartSpeech)

By default the speech recognition plugin does not start listening for speech until you invoke HI.startSpeechRec(). You can later stop listening for speech by calling HI.stopSpeechRec(). If you want speech recognition to start immediately after HumanInput is instantiated supply the autostartSpeech = true setting:

var settings = {autostartSpeech: true};
var HI = new HumanInput(window, settings);
Note

Speech recognition will automatically be paused when the document becomes hidden and resumed when it becomes visible (active) again.

Feedback Plugin

Feedback Plugin Example

The HumanInput Feedback plugin (which is automatically included in the '-full' version of humaninput.js) adds support for providing visual, audio, and vibration feedback for triggered events. You can enable each feedback type via the visualFeedback, audioFeedback, and vibrationFeedback settings:

var settings = {visualFeedback: true, audioFeedback: true, vibrationFeedback: true};
var HI = new HumanInput(window, settings);

You can specify the element to display visual feedback via the feedbackElem setting:

var settings = {visualFeedback: true, feedbackElem: '#my_feedback_element'};
var HI = new HumanInput(window, settings);

If you do not specify an element HumanInput will automatically add a #hi_feedback div to the document.body of the web page in question along with a basic <style> tag.

Note

Vibration feedback is really only useful on mobile devices so by default it is only enabled for pointer:down and pointer:up. Also, it is much more useful as a debugging tool than an actual user interaction tool.

You can specify which events apply to the three different feedback types via the visualEvents, audioEvents, and vibrationEvents settings. Example:

// Only display visual feedback for keydown events
var settings = {visualFeedback: true, visualEvents: ['keydown']};
var HI = new HumanInput(window, settings);

Customizing/Developing HumanInput

So you want a custom version eh? Piece of cake! You just need to clone this repo (you probably already did that) and install a few things:

# You need the Node Package Manager and make (you probably already have it):
sudo apt-get install npm make
# Install (latest) webpack globally as a command line tool:
sudo npm install webpack@2.1.0-beta.15 -g
npm install # Install dependencies (locally in the HumanInput dir)

Now you're ready to build HumanInput. Just run make (or npm run build) and you should see something like this:

Example running make

Tip:

You can run make dev and make prod to build unminified and minified versions, respectively. There's also 'scripts' for npm so you can run npm run build:dev and npm run build:prod too.

Customizing

To build a custom version of HumanInput with just the things you want just edit src/humaninput-full.js and comment out the features you don't want. For example, let's say you want everything but the Speech Recognition plugin. Just delete that import line or turn it into a comment like this:

// Speech recognition
//import SpeechRecPlugin from './speechrec';

Now when you run make the '-full.js' version of HumanInput will include everything but the Speech Recognition feature.

About

A JavaScript library for handling keyboard shortcuts and other human-generated events

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages