This page is a blank site that pulls in dave-widget.js with a single <script> tag. The head floating in the bottom-right corner follows your cursor. Below are the patterns for using Dave on a real site.
One-liner in <head>. The widget infers where the sprites live from the script's own URL (it looks for sprites/ next to itself). That's what's running on this page.
<script src="/dave-widget.js" data-auto="bottom-right" data-size="140"></script>
Attributes: data-auto = corner (top-left, top-right, bottom-left, bottom-right, inline), data-size = pixel height, data-react=false to disable click reactions, data-base to override sprite location.
Use <dave-face frame="…"> as a regular HTML tag. No JS call needed.
<dave-face frame="expressions/happy" size="48"></dave-face>
Dave"Are we sure this is going to scale?"
Dave"Okay that's actually hilarious 😂"
Three entrypoints on the global DaveWidget:
// Set base URL once (optional; auto-inferred otherwise) DaveWidget.configure({ base: '/assets/dave/sprites' }); // Mount a follower imperatively (returns a controller) const widget = DaveWidget.follow({ position: 'top-right', size: 120, offset: { x: 16, y: 16 }, react: true, }); // widget.destroy(); // remove it later // Build a static <img> for a single frame const img = DaveWidget.image('expressions/wink', { size: 64 }); document.querySelector('.avatar').appendChild(img); // Enumerate what's available console.log(DaveWidget.frames.expressions); // → ['happy','laugh','shocked', ... 18 total]
The follower exposes .play(name) for firing a named animation. Cursor tracking pauses while the sequence runs. Built-ins cover most reactions; register custom ones with DaveWidget.defineSequence(name, steps).
// Programmatic DaveWidget.__auto.play('wink'); DaveWidget.play('excited'); // shortcut targeting the auto-mounted widget DaveWidget.play([ // inline custom sequence { frame: 'expressions/disgust', ms: 420 }, { frame: 'expressions/suspicious', ms: 260 }, ]); // Register a named custom sequence, reusable anywhere DaveWidget.defineSequence('celebrate', [ { frame: 'expressions/laugh_big', ms: 240 }, { frame: 'expressions/exclaim', ms: 300 }, { frame: 'expressions/happy', ms: 220 }, ]); // HTML-only: any element with data-dave-play fires on click <button data-dave-play="wink">Say hi</button>
These buttons use data-dave-play — no handler wiring, the widget owns the delegation. Any click inside [data-dave-play] fires that sequence on the auto-mounted Dave.
Every motion knob is configurable. Drag the sliders — they reach into the auto-mounted follower via controller.update({…}) and take effect immediately.
DaveWidget.follow({
maxLean: 10, // px the image can shift toward the cursor (0 disables)
maxLeanY: 6, // vertical lean; default = maxLean * 0.6
maxTilt: 3, // degrees of head tilt (0 disables)
deadZone: 0.2, // cursor must pass this fraction of image size to swap angle
reach: 0.6, // cursor distance that maps to full lean; lower = more sensitive
flip: true, // mirror sprite when cursor is on the left
follow: true, // master switch; false = locked forward
});
You can also set these on the <script> tag via data-max-lean, data-max-tilt, data-dead-zone, data-reach, data-flip, data-follow.
Every shipped frame, rendered declaratively: