StringTune Event API

StringTune ships with a lightweight event bus shared by every module. Subscribe through the singleton instance, and tap into per-object events via an individual StringObject.

import StringTune from '@fiddle-digital/string-tune';

const stringTune = StringTune.getInstance();
const unsubscribe = (value: number) => {
  console.log('Scroll position', value);
};

stringTune.on('scroll', unsubscribe);

// Later
stringTune.off('scroll', unsubscribe);

Need the object itself? Reuse the engine’s registry:

const stringTune = StringTune.getInstance();
const hero = stringTune['objectManager'].all.get('hero');

hero?.events.on('enter', (object) => {
  console.log('Hero entered view', object.id);
});

Object Lifecycle (per-object)

These hooks live on StringObject.events.

EventPayloadDescription
enterStringObjectObject became active.
leaveStringObjectObject became inactive.

Subscribe with object.events.on('enter', handler).


Intersection & In-View Events

EventPayloadDescription
object:activate:<id>booleanIntersection observer toggled the object (true when active).
object:inview:<id>{ inView: boolean, direction: 'enter-top' | 'enter-bottom' | 'exit-top' | 'exit-bottom' | null }Fired when scroll crosses the in-view window defined by offsets.
const stringTune = StringTune.getInstance();

stringTune.on('object:activate:hero', (isActive) => {
  console.log('Hero active?', isActive);
});

stringTune.on('object:inview:gallery', ({ inView, direction }) => {
  console.log('Gallery in view:', inView, 'direction:', direction);
});

Scroll & System Events

EventPayloadDescription
startnullEmitted after initialisation completes.
scrollnumberCurrent scroll position (pixels).
lerpnumberSmoothed scroll velocity (same easing used for motion modules).
updatenullFired every frame after modules have mutated DOM/CSS.
scroll:startnullWheel/touch input begins.
scroll:stopnullVelocity drops below the stop threshold.
scroll:direction:changebooleantrue when scrolling down, false when scrolling up.
wheelWheelEventForwarded wheel events from elements using string="scroller".

Progress, Parallax, and Motion

EventPayloadEmitted ByNotes
object:progress:<id>number (0–1)StringProgressEased progress applied to DOM and mirrors.
object:progress-slice:<id>number (0–1)StringProgressPartRemapped sub-range progress for string-part-of.
object:parallax:<id>number (pixels)StringParallaxCurrent Y translation.
object:lerp:<id>numberStringLerpScroll velocity applied to the element.
object:glide:<id>number (pixels)StringGlideGlide displacement; resets to 0 on scroll stop.
const stringTune = StringTune.getInstance();

stringTune.on('object:progress:hero', (progress) => {
  progressBar.style.setProperty('--hero-progress', progress.toFixed(3));
});

stringTune.on('object:parallax:hero', (translation) => {
  heroSection.style.transform = `translateY(${translation}px)`;
});

Cursor, Magnetic, Spotlight & Impulse

Cursor Module

EventPayloadDescription
cursor:start:<id>nullPointer begins moving over the element (above epsilon).
cursor:move:<id>{ x: number, y: number }Normalized coordinates (respects string-alignment setting).
cursor:pixel:<id>{ x: number, y: number }Smoothed pixel offsets relative to element's top-left.
cursor:end:<id>nullPointer stops moving (settles below epsilon).
cursor{ x: number, y: number, stepX: number, stepY: number }Global smoothed cursor position in viewport pixels with deltas.

Magnetic Module

EventPayloadDescription
magnetic:move:<id>{ x: number, y: number }Magnetic offset currently applied to item.

Spotlight Module

EventPayloadDescription
spotlight:update:<id>{ angleDeg: number, distance: number }Spotlight direction in degrees and distance.

Impulse Module

EventPayloadDescription
object:impulse:<id>:move{ x: number, y: number }Current push translation.
object:impulse:<id>:rotate{ rotation: number }Rotation in degrees.
object:impulse:<id>:side{ value: number }Cursor side factor (0–1) under the pointer.
const stringTune = StringTune.getInstance();

stringTune.on('cursor:move:cta-button', ({ x, y }) => {
  ctaButton.style.setProperty('--cursor-x', x.toString());
  ctaButton.style.setProperty('--cursor-y', y.toString());
});

stringTune.on('magnetic:move:cta-button', ({ x, y }) => {
  ctaButton.style.transform = `translate(${x}px, ${y}px)`;
});

Form Module

EventPayloadDescription
form:submit:<id>Record<string, any>Serialised form data after successful validation.
form:invalid:<id>undefinedFired when validation fails on submit.

Lazy Loading

EventPayloadDescription
image:load:allnullAll managed lazy images finished loading.

Responsive Breakpoints

These events replay the last payload for new subscribers.

EventPayloadDescription
screen:mobilebooleanMobile media query toggled.
screen:tabletbooleanTablet range toggled.
screen:laptopbooleanLaptop range toggled.
screen:desktopbooleanDesktop range toggled.

Slider Sequences

StringSequence listens for a global sequence event. Emit it when your slider advances.

const stringTune = StringTune.getInstance();
stringTune.emit('sequence', { slider: 'gallery-slider', step: 2 });
EventPayloadDescription
sequence{ slider: string, step: number }Drives StringSequence state syncing.

Cleaning Up

Pair every subscription with stringTune.off(event, handler) when you no longer need it. For per-object hooks, call object.events.off(event, handler) or destroy the object to clear listeners automatically.