# 🔌 Plugins and the main loop
When you run handsfree.start(callback) a loop is started that does 4 things:
- Synchronously updates all models and stores their data in
handsfree.model[modelName].data
- Triggers a
handsfree-data
event on thedocument
with an object containing all the data - Runs all active plugins stored in
handsfree.plugin[pluginName]
- Runs the callback after all models have been loaded
"Plugins" are the recommended way of working with Handsfree. All enabled plugins run their logic on every frame, can be attached to specific models, toggled on/off by tag, and can even manipulate your handsfree
instance and other plugins.
# Basic plugins
Basic plugins are created with handsfree.use(pluginName, callback):
// A plugin that console logs your data on every frame
handsfree.use('consoleLogger', (data) => {
console.log(data.weboji.rotation, data.pose.data.faceLandmarks)
})
The above will create a new plugin that can now be accessed with handsfree.plugin.consoleLogger
and will be run on every frame. The callback is stored in handsfree.plugin.consoleLogger.onFrame
, and you can toggle the plugin with:
handsfree.plugin.consoleLogger.enable()
handsfree.plugin.consoleLogger.disable()
The callback receives a data
object containing the data of every enabled model. It's common to simply destructure the model that you need:
// A plugin that console logs your data on every frame
handsfree.use('consoleLogger', ({weboji}) => {
console.log(weboji.rotation, weboji.translation, weboji.morphs)
})
# Complex plugins
Instead of passing a function you can pass an object into handsfree.use(pluginName, opts). opts
can contain anything, but the following lists special properties and methods:
handsfree.use('pluginName', {
// Whether the plugin is enabled or not
// - If omitted, this will default to true
enabled: true,
// A list of strings that can be used to toggle this plugin on/off (see below)
tags: [],
// A set of config values that can be overwritten
// - These can be set during instantiation (see below)
config: {},
// Called on every frame
// - If you only pass a callback, it'll get assigned to this method
onFrame (data) {},
// Called immediate after this plugin is added (whether it's enabled or not)
// - Use this for any setup, like creating DOM elements
onUse (handsfree) {},
// Called when the plugin is enabled from a disabled state
// - eg, after calling: handsfree.plugin.myPlugin.enable()
onEnable (handsfree) {},
// Called when the plugin is disabled from an enabled state
// - eg, after calling: handsfree.plugin.myPlugin.disable()
onDisable (handsfree) {}
})
Since the basic plugin above simply maps the callback to an onFrame
method, another way of writing it is:
handsfree.use('consoleLogger', {
onFrame (data) {
console.log(weboji.rotation, weboji.translation, weboji.morphs)
}
})
The onEnable
and onDisable
methods makes it easy to run code when you run .enable()
or .disable()
on the plugin. A common use case is to show/hide DOM elements (for example, to toggle the face and palm pointers).
You can add tags to the plugin through the .tags
prop to bulk disable/enable plugins with the handsfree.enablePlugins(tags) and handsfree.disablePlugins(tags). .tags
can be a string or array of strings:
handsfree.use('consoleLogger', {
// This can be a string or array of strings
tags: ['debugging'],
onFrame (data) {
console.log(data)
}
})
// Toggle the core "browser" plugins that come with Handsfree.js
// - These help you use webpages handsfree
// - Note that the consoleLogger will still be active since it doesn't have a browser tag
handsfree.enablePlugins('browser')
handsfree.disablePlugins('browser')
Calling these methods without tags enables or disables all the plugins. This provides a powerful way to swap out plugins based on the route:
// Disable all the plugins
handsfree.disablePlugins()
// Enable only the plugins for the current route
handsfree.enablePlugins('homepage')
Note:
.onEnable()
is only called when you runhandsfree.plugin[pluginName].enable()
. It doesn't run immediately when you.use()
the plugin even if.enabled === true
. UseonUse()
instead.
# Accessing data outside of a plugin
You may occasionally find yourself in a part of your app where you don't have easy access to your handsfree
instance. In these cases, you can still access the data on every frame by listening to the handsfree-data
event on the document
:
document.addEventListener('handsfree-data', (event) => {
console.log(event.detail.weboji.rotation, event.detail.weboji.translation)
})
# Checking your data
Because you have the ability to swap out models and configs on the fly, it's highly recommended to first check that the model has data. Due to the synchronous way that data is received it's possible that the data is not present the frame immediately after enabling the plugin:
handfree.use('consoleLogger', (data) => {
if (!data.weboji) return
console.log(data.weboji)
})
# Updating plugins through instantiation or handsfree.update()
You can set plugins during instantiation through the plugin
property. Set the plugin name to true to use defaults:
handsfree = new Handsfree({
hands: true,
plugin: {
// Enable with defaults
pinchScroll: true
}
})
Or pass a config object to pre-configure it:
handsfree = new Handsfree({
hands: true,
plugin: {
pinchScroll: {
enabled: true,
speed: 1
}
}
})
You can do the same with the handsfree.update() method:
handsfree.update({
plugin: {
pinchScroll: true
}
})