humanlatency
v1.0.0
Published
**Make interfaces feel human, not instant.**
Readme
HumanLatency.js
Make interfaces feel human, not instant.
UI frameworks have made digital interactions incredibly fast, but sometimes too fast. Perfectly instant reactions can feel sterile, robotic, or overwhelming. HumanLatency.js introduces adaptive, human-like micro-delays (80–280ms) as a behavior layer to your UI, making it feel more organic.
It is not an animation library or a performance tool. It is a human perception and behavior layer.
Features
- Micro-Delays: Adds tiny, perceptible pauses before executing actions.
- Probabilistic Variance: Natural, bell-curve randomized variance (not just raw
Math.random()) so delays don't feel mechanic. - Intent-Based: Different delays for different actions (
primary,secondary,destructive,exploratory). - Interaction Rhythm: Tracks how fast the user is interacting. Speeds up for consistent pacing, slows down for rapid spamming, and adds slight pause after long idle times.
- Profiles: Choose between
snappy,balanced, orcalmbase configurations.
Installation
You can install via npm:
npm install humanlatencyBasic Usage
Using the DOM Adapter (Vanilla JS)
You can easily wrap any DOM element's click event so the action fires after the micro-delay.
import { hl } from "humanlatency";
const button = document.getElementById("my-button");
// Wrap the button click
hl.wrap(
button,
(e) => {
console.log("Action executed with human rhythm!");
},
{ intent: "primary" },
);Promises & Async/Await
You can manually await the latency engine before performing logical operations.
import { hl } from "humanlatency";
async function performDangerousAction() {
// Wait using "destructive" profile (~180ms + variance)
await hl.sleep("destructive");
deleteUserAccount();
}
// Or use the runner wrapper:
async function loadData() {
const data = await hl.run(
() => fetch("/api/data").then((res) => res.json()),
"exploratory",
);
}Configuration & Profiles
HumanLatency.js comes with three profiles:
snappy: Max delay 120ms. Very subtle, minimal friction.balanced(Default): Max delay 280ms. Noticeable organic rhythm.calm: Max delay 500ms. Heavy friction, forces the user to pace themselves.
Customizing the Profile
import { HumanLatency } from "humanlatency";
// Create a custom engine instance
const myEngine = new HumanLatency({
profile: "calm",
});
myEngine.wrap(button, callback);Latency Groups
If you have specific areas of the UI that should share the exact same rhythm state and configuration (like a specific toolbar), you can create isolated groups.
import { createLatencyGroup } from "humanlatency";
const toolbarLatency = createLatencyGroup({ profile: "snappy" });
toolbarLatency.wrap(btn1, action1);
toolbarLatency.wrap(btn2, action2);Intents
When calculating delay, you pass an intent argument which establishes the baseline delay before heuristics and variance apply:
primary: Standard clicks (e.g., Submit, Proceed) ~100mssecondary: Minor contextual clicks (e.g., Cancel, Close) ~120msdestructive: Irreversible actions (e.g., Delete, Remove) ~180msexploratory: Low-stakes discovery clicks (e.g., Tabs, Accordeons) ~90ms
Debug Mode
Need to see the math happening under the hood?
import { hl } from "humanlatency";
hl.setDebug(true);Outputs to console: [HumanLatency] intent=primary, base=100, rhythm=0, var=12.00 => final=112ms
Kill Switch
You can disable the entire latency engine globally if required (e.g., for automated testing).
import { hl } from "humanlatency";
hl.disable(); // Delays become 0 immediately
hl.enable(); // Restores delays