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 arrow

View on GitHub (pinned to 13e2b79054)

Solutions

  1. Set keyboard:false on all but one instance
  2. Kill the previous instance's keyboard ownership by killing that instance
  3. 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

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


AI-assisted analysis of greensock/GSAP@13e2b79054 (2026-08-29). Data as JSON: /api/errors/13734dd45d254899. Report an issue: GitHub.