Keys.js powers dynamic, CSS-driven keyboard-reactive effects using CSS variables and key press data, making interactive keyboard experiences effortless. Create custom animations in CSS or use keys-animations.css for ready-made effects—full control or quick setup. Perfect for gaming, interactive applications, and keyboard-driven UIs.
See it in action here: https://idev-games.github.io/Keys-JS/
Keys-Animations.css documentation can be found here: https://idev-games.github.io/Keys-JS/animations.html
Download from Github or install with NPM:
npm i @idevgames/keys-jsUse Keys.js from a CDN:
<script src="https://cdn.jsdelivr.net/npm/@idevgames/keys-js/src/keys.min.js"></script>Keys.js is a super simple, efficient and lightweight way of exposing keyboard input to CSS variables. You can use Keys.js to create keyboard-reactive animations, game controls, shortcuts, and interactive experiences entirely in CSS.
Using nothing but the power of CSS, HTML and Keys.js, you can make keyboard-driven games, interactive demos, and reactive UIs. Keys.js is really lightweight and created with vanilla JavaScript with zero dependencies.
Keys.js is designed to work seamlessly with other iDev animation libraries:
- Motion.js - Time-based animations
- Trig.js - Scroll-based animations
- Cursor.js - Mouse position tracking
Together, they form a complete CSS-native animation ecosystem for modern web development and gaming.
Add Keys.js to your HTML:
<script src="keys.js"></script>Add data-keys to any element you want to track:
<div class="box" data-keys data-keys-watch="space" data-keys-var="true"></div>Style it with CSS using CSS variables:
.box {
transform: scale(1);
transition: transform 0.2s ease;
}
/* Scale up when space is pressed */
.box.key-space {
transform: scale(1.5);
}
/* Or use CSS variables directly */
.box {
transform: scale(calc(1 + var(--key-space, 0) * 0.5));
}Keys.js is optimized for gaming with features that make it perfect for interactive applications:
Essential for games - prevents arrow keys from scrolling, space from page-down, etc.
Recommended: Per-element prevention (granular control)
<!-- Only prevent defaults for specific keys on specific elements -->
<div data-keys data-keys-watch="up,down,space" data-keys-prevent="up,down,space">
<!-- Arrow keys and space won't scroll when this element is active -->
</div>Alternative: Global prevention (for full-screen games)
<!-- Prevent defaults for all keys globally -->
<body data-keys-prevent-defaults="*">
<!-- Or specific keys globally -->
<body data-keys-prevent-defaults="up,down,left,right,space">The data-keys-prevent attribute gives you fine-grained control and only prevents defaults when needed, making it easier to develop and test.
Keys.js uses performance.now() instead of Date.now() for sub-millisecond precision. Perfect for frame-perfect input detection.
// Get exact hold duration in milliseconds
const holdDuration = keys.getKeyHoldDuration('space');
if (holdDuration > 500) {
// Charged attack ready!
}Keys.js uses non-passive event listeners to support preventDefault() while maintaining responsiveness. This is critical for gaming where you need to override browser shortcuts.
Keys.js tracks the first press separately from key repeats, ensuring accurate press counting and timing:
// Press count only increments on initial keydown, not repeats
const pressCount = keys.getKeyPressCount('space');Keys.js exposes keyboard state as CSS variables that update in real-time:
/* Key state (0 or 1) */
var(--key-space) /* Is space pressed? */
var(--key-w) /* Is W pressed? */
var(--key-enter) /* Is enter pressed? */
/* Press count */
var(--key-space-count) /* How many times pressed */
/* Hold duration */
var(--key-shift-duration) /* Hold time in milliseconds (e.g. "1234ms") */
var(--key-shift-hold) /* Normalized 0-1 value (capped at 1 second) */
/* Modifier keys */
var(--key-shift) /* Is shift pressed? */
var(--key-ctrl) /* Is ctrl pressed? */
var(--key-alt) /* Is alt pressed? */
var(--key-meta) /* Is meta/cmd pressed? */
/* Active keys count */
var(--keys-active) /* Number of keys currently pressed */<div class="charge-bar" data-keys data-keys-watch="shift" data-keys-var="true">
<div class="charge-fill"></div>
</div>.charge-fill {
/* Width grows from 0 to 100% based on hold duration (0-1) */
width: calc(var(--key-shift-hold, 0) * 100%);
height: 10px;
background: linear-gradient(90deg, #700c0c, #ff8800);
transition: width 0.05s linear;
}
/* Full charge indicator */
[data-key-shift-hold="1"] .charge-fill {
box-shadow: 0 0 20px #ff8800;
animation: pulse 0.3s ease infinite;
}Keys.js automatically adds classes to elements for easy CSS targeting:
/* General classes */
.keys-active /* Added when ANY key is pressed */
.keys-typing /* Added when user is typing */
.keys-idle /* Added when no keys pressed */
/* Specific key classes */
.key-space /* Added when space is pressed */
.key-enter /* Added when enter is pressed */
.key-w /* Added when W is pressed */
.key-up /* Added when up arrow is pressed */
/* Body classes for key count breakpoints */
.keys-count-1 /* At least 1 key pressed */
.keys-count-2 /* At least 2 keys pressed */
.keys-count-3 /* At least 3 keys pressed */
.keys-count-5 /* At least 5 keys pressed */
.keys-count-10 /* At least 10 keys pressed */Example:
.player {
background: blue;
}
/* Turn red when space is pressed */
.player.key-space {
background: red;
transform: translateY(-50px);
}Configure Keys.js behavior with HTML attributes:
<div id="player"
data-keys
data-keys-watch="w,a,s,d,space"
data-keys-var="true"
data-keys-global="false"
data-keys-combo="shift+space"
data-keys-prevent="space,w,a,s,d">
</div>| Attribute | Description | Example |
|---|---|---|
data-keys |
Enable Keys.js on element | data-keys |
data-keys-watch |
Comma-separated list of keys to track | data-keys-watch="w,a,s,d" |
data-keys-var |
Enable CSS variables | data-keys-var="true" |
data-keys-global |
Set variables on :root instead of element |
data-keys-global="true" |
data-keys-combo |
Define key combo to detect | data-keys-combo="ctrl+shift" |
data-keys-prevent |
Prevent default browser behavior for specific keys | data-keys-prevent="up,down,space" |
data-keys-prevent-defaults |
Keys to prevent default behavior (body only, legacy) | data-keys-prevent-defaults="*" |
Keys.js comes with a ready-to-use animation library with 30+ keyboard-triggered animations:
<link rel="stylesheet" href="keys-animations.css">
<div data-keys data-keys-watch="space" class="keys-anim-press-pulse">
Press SPACE to see pulse animation
</div>Animation Categories:
- Press Animations: pulse, shake, bounce, glow, flash
- Arrow Keys: arrow-up, arrow-down, arrow-left, arrow-right
- WASD Movement: wasd-up, wasd-down, wasd-left, wasd-right
- Specific Keys: space-jump, enter-confirm, escape-fade, tab-switch
- Typing Effects: typing-glow, typing-scale, typing-shake
- Combos: combo-flash, combo-spin, combo-burst
- Rotation: rotate-r, rotate-continuous
- Color Effects: color-shift, brightness-pulse, fade-in
See all animations: https://idev-games.github.io/Keys-JS/animations.html
Keys.js works seamlessly with other iDev libraries for complete CSS-native game development:
<div class="player"
data-keys
data-keys-watch="w,a,s,d,space"
data-keys-var="true"
data-motion
data-motion-var="true">
</div>.player {
position: absolute;
/* Position based on WASD key press count */
left: calc(50% + (var(--key-d-count, 0) - var(--key-a-count, 0)) * 10px);
top: calc(50% + (var(--key-s-count, 0) - var(--key-w-count, 0)) * 10px);
/* Idle animation from Motion.js */
transform: translateY(calc(var(--motion-bounce, 0%) * 0.05));
}
/* Jump on space */
.player.key-space {
transform: translateY(-50px);
transition: transform 0.2s ease-out;
}See codepen examples for comprehensive integration examples.
- Use
data-keys-watchto limit which keys are tracked per element - Prevent defaults only when needed - use specific keys, not
*for regular websites - Use CSS transitions instead of JavaScript for smooth animations
- Leverage
requestAnimationFramefor game loops, not setInterval - Combine with Motion.js for time-based effects without JavaScript animation loops
Keys.js works in all modern browsers:
- ✅ Chrome 90+
- ✅ Firefox 88+
- ✅ Safari 14+
- ✅ Edge 90+
Uses standard APIs:
- Keyboard Events (KeyboardEvent)
- Performance API (performance.now())
- CSS Custom Properties
- requestAnimationFrame
Keys.js automatically normalizes key names for consistency:
| Browser Key | Normalized Name |
|---|---|
| ' ' (space) | space |
| 'ArrowUp' | up |
| 'ArrowDown' | down |
| 'ArrowLeft' | left |
| 'ArrowRight' | right |
| 'Control' | ctrl |
| 'Meta' | meta |
| 'Shift' | shift |
| 'Alt' | alt |
| 'Enter' | enter |
| 'Escape' | esc |
| 'Backspace' | backspace |
| 'Tab' | tab |
All other keys use their lowercase event.key value.
Keys.js is part of the iDev Animation Toolkit Ecosystem:
🎹 Keys.js (You are here)
Purpose: Keyboard-reactive CSS variables Best For: Gaming controls, keyboard shortcuts, interactive applications
⏱️ Motion.js
Purpose: Time-reactive CSS variables Best For: Loading animations, ambient motion, continuous effects
📜 Trig.js
Purpose: Scroll-reactive CSS variables Best For: Parallax effects, scroll reveals, storytelling websites
🖱️ Cursor.js
Purpose: Mouse position tracking Best For: Cursor effects, mouse-reactive animations
Together they provide complete control over input, time, and state - all through CSS!
MIT License - Free for commercial and personal use

