Custom Modules
Lifecycle Hooks
Accurate map of which module hooks run, in what phase, and which ones are object-specific versus global.
Lifecycle Hooks
StringTune exposes many hooks on StringModule, but not all of them are equally important, and not all of them are dispatched in the same way.
This page describes the current runtime behavior, not the theoretical interface.
Registration hooks
These run once per module instance:
| Hook | When it runs | Typical use |
|---|---|---|
onSubscribe() | immediately after stringTune.use(...) registers the module | subscribe to global event channels |
onInit() | during stringTune.start(...) after the runtime starts | one-time setup that needs the runtime to be alive |
onUnsubscribe() | during runtime teardown | remove global subscriptions |
destroy() | after unsubscribe during teardown | clear module-owned state |
Object hooks
These run per connected StringObject:
| Hook | When it runs | Typical use |
|---|---|---|
initializeObject(...) | when the object is first processed or refreshed | map attributes and seed per-object state |
calculatePositions(...) | after initialization and on rebuild paths | derive geometry, ranges, and cached values |
onObjectConnected(...) | on first connection to that object | attach listeners, apply initial output |
onObjectDisconnected(...) | when the object is removed from the module | cleanup object-owned side effects |
enterObject(...) | when the object becomes entered or active | maintain hot-path collections |
exitObject(...) | when the object leaves active scope | maintain hot-path collections |
The default connectObject(...) implementation already calls onObjectConnected(...) only on the first connection.
Frame and interaction hooks
These are the hooks you will use most often:
| Hook | Runtime phase | Receives | Best for |
|---|---|---|---|
onFrame(data) | every frame | StringData | pure computation and state updates |
onScroll(data) | when scroll changed | StringData | scroll-dependent bookkeeping |
onScrollMeasure(data) | scheduled read phase after scroll changes | StringData | layout reads tied to scroll |
onMouseMove(event) | every mouse move | MouseEvent | cursor-driven target updates |
onMouseMoveMeasure(data) | scheduled read phase after mouse move | StringData | pointer-related DOM reads |
onMutate(data) | mutate phase inside a styleTxn batch | StringData | DOM writes, CSS vars, inline styles |
onWheel(event) | wheel input | WheelEvent | wheel-specific behavior separate from scroll |
Resize and system hooks
| Hook | When it runs | Notes |
|---|---|---|
onResize() | layout rebuild path | use for per-object recalculation that is not already handled in initializeObject(...) |
onResizeWidth() | width changed or forced resize | useful for responsive-only recalculation |
onSettingsChange() | after setupSettings(...) changes | current runtime calls it with no payload |
onDirectionChange() | scroll direction changed | currently dispatched |
onScrollStart() | scroll begins | currently dispatched |
onScrollStop() | scroll stops | currently dispatched |
onAxisChange() | reserved hook | present on base class |
onDeviceChange() | reserved hook | present on base class |
onScrollConfigChange() | reserved hook | present on base class |
onDOMMutate(added, removed) | DOM observer sees mutations | useful for sidecar scanning |
onDOMRebuild() | object manager rebuilds all modules after DOM work | current runtime dispatches this from ObjectManager |
Important current-runtime caveats
Two details matter when writing real custom modules:
onSettingsChange()is currently called without the internal change payload. If you need detailed width, height, or scroll-height flags, the module hook does not receive them.onScrollDirectionChange()exists on the base class, but the current runtime does not dispatch it. UseonDirectionChange()instead.
Which hook should own which work
Use this rule of thumb:
initializeObject(...)for parsing and caching object statecalculatePositions(...)for geometry mathonFrame(...)for cheap pure computationsonScrollMeasure(...)andonMouseMoveMeasure(...)for DOM readsonMutate(...)for DOM writesonObjectConnected(...)andonObjectDisconnected(...)for listener and observer management
If a module mixes reads and writes into onFrame(...), it will usually work at first and then degrade as more objects are added.