greensock/GSAP · warning
[GSDevTools warning] only one instance can be affected by ke
Error message
[GSDevTools warning] only one instance can be affected by keyboard shortcuts. There is already one active.
What it means
Keyboard shortcuts (spacebar, arrows) are wired globally to one GSDevTools instance at a time. If a second instance enables keyboard while one already holds _keyboardInstance, GSDevTools warns that only one instance can respond to the keyboard.
Source
Thrown at src/GSDevTools.js:1029
_addListener(playPauseButton, "mousedown", togglePlayPause);
_addListener(find(".seek-bar"), "mousedown", onPressSeekBar);
_addListener(find(".rewind"), "mousedown", onPressRewind);
_addListener(loopButton, "mousedown", toggleLoop);
_addListener(timeScale, "change", onChangeTimeScale);
if (vars.visibility === "auto") {
_addListener(root, "mouseout", onMouseOut);
//_addListener(find(".gs-hit-area"), "mouseover", show);
_addListener(root, "mouseover", show);
} else if (vars.visibility === "hidden") {
hidden = true;
autoHideTween.progress(1);
}
if (vars.keyboard !== false) {
if (_keyboardInstance && vars.keyboard) {
console.warn("[GSDevTools warning] only one instance can be affected by keyboard shortcuts. There is already one active.");
} else {
_keyboardInstance = _self; //we can't have multiple instances all affected by the keyboard.
keyboardHandler = e => { //window.parent allows things to work inside of an iframe, like on codepen.
let key = e.keyCode ? e.keyCode : e.which,
ts;
if (key === 32) { //spacebar
togglePlayPause();
} else if (key === 38) { //up arrow
ts = parseFloat(_shiftSelectedValue(timeScale, -1, timeScaleLabel));
linkedAnimation.timeScale(ts);
record("timeScale", ts);
} else if (key === 40) { //down arrow
ts = parseFloat(_shiftSelectedValue(timeScale, 1, timeScaleLabel));
linkedAnimation.timeScale(ts);
record("timeScale", ts);
} else if (key === 37) { //left arrow
onPressRewind(e);
} else if (key === 39) { //right arrowView on GitHub (pinned to 13e2b79054)
Solutions
- Set keyboard:false on all but one instance
- Kill the previous instance's keyboard ownership by killing that instance
- Ensure only one GSDevTools instance exists at a time
Example fix
// before
GSDevTools.create({ animation: tl1 });
GSDevTools.create({ animation: tl2 }); // second warns
// after
GSDevTools.create({ animation: tl1 });
GSDevTools.create({ animation: tl2, keyboard: false }); Defensive patterns
Strategy: validation
Validate before calling
if (!document.querySelector('.gs-dev-tools')) { GSDevTools.create({ animation, keyboard: true }); } else { GSDevTools.create({ animation, keyboard: false }); } Type guard
const keyboardIsFree = () => !GSDevTools.getByAnimation(gsap.globalTimeline) || true; // track your instances in app state instead
Prevention
- Enable keyboard on exactly one instance
- Remember keyboard defaults to enabled — set keyboard:false on extra instances
- Track created instances in app state and reuse instead of recreating
When it happens
Trigger: Creating two GSDevTools instances with vars.keyboard !== false (or explicitly true) without killing or disabling the first; the default (keyboard enabled) makes two plain instances collide.
Common situations: Multiple dev tool panels on one page during debugging; component-based apps instantiating a tool per component; forgetting keyboard defaults to on.
Related errors
- Error: GSDevTools can only have one instance that's globally
- GSDevTools error: invalid animation.
- Please gsap.registerPlugin(CSSPlugin, CSSRulePlugin)
- Please gsap.registerPlugin(CustomEase, CustomBounce)
- Please gsap.registerPlugin(CustomEase)
AI-assisted analysis of greensock/GSAP@13e2b79054 (2026-08-29).
Data as JSON: /api/errors/13734dd45d254899.
Report an issue: GitHub.