PyEngine (f8.pyengine)
Python-based execution engine for Feel8 operators.
- Service class:
f8.pyengine
- Version:
0.0.1
- Source directory:
f8/engine
- Tags:
engine, python, py
When to Use
- Use
f8.pyengine as the main host for operator-driven logic inside Feel8 graphs.
- It is the standard place for chaining transforms, triggers, state machines, and custom orchestration logic.
- Once a graph needs a real logic layer,
PyEngine is usually central to it.
Common Wiring Patterns
- Start by placing one or more
PyEngine service nodes.
- Every hosted operator must point its
Service Id at the intended engine host.
- As graphs grow, split engines by purpose, such as one for vision and one for device control.
Pitfalls / Gotchas
- If operators appear correctly wired but never run, check
Service Id first.
- Long-running or blocking logic inside one engine can stall the entire host.
- When one host becomes too large or latency grows, split the workload rather than continuing to pile onto one engine.
Service Reference
How to Run
pixi run -e default f8pyengine
- Workdir:
../../../
- Environment overrides: none
- Data inputs: none
- Data outputs:
monitor
- Commands: none
Service State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
dataDelivery |
rw |
true |
true |
string / enum[pull, push, both] / default=pull |
How data inputs are delivered to nodes: pull (default), push, or both. |
active |
rw |
true |
false |
boolean / default=True |
Service lifecycle state (activate/deactivate). |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
Key Fields That Matter
dataDelivery (Data Delivery, rw): How data inputs are delivered to nodes: pull (default), push, or both. Schema: string / enum[pull, push, both] / default=pull.
active (Active, rw): Service lifecycle state (activate/deactivate). Schema: boolean / default=True.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
Service Commands
None
None
Service Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
monitor |
true |
false |
object{active, alive, cpu, error, ...} |
Unified runtime monitor snapshots (health/resource/perf/error). |
Operators
Tick (f8.tick)
Source operator that generates periodic exec ticks.
When to Use
- Use
Tick when your graph needs a simple, periodic execution (exec) trigger to drive deterministic update loops.
- It is the standard root node for most
f8.pyengine logic chains, ensuring that your operators run at a consistent frequency regardless of the UI's frame rate.
- Ideal for polling sensors, updating state machines, or sending periodic commands to hardware.
Common Wiring Patterns
- Logic Heartbeat: Connect the
tick output to a Sequence operator to drive multiple branches (Read -> Process -> Write) in a predictable order every cycle.
- Hardware Sync: Align the
tickMs property (e.g., 20ms for 50Hz) with the expected interval of your downstream device nodes (like f8-tcode.intervalMs) to minimize jitter.
- Performance Branching: Use different
Tick nodes with different frequencies (one "fast" for motion, one "slow" for status checks) to optimize CPU usage.
Pitfalls / Gotchas
- Scheduling Overload: Setting a very small
tickMs (e.g., 1ms) can overwhelm the engine thread if the graph is complex, leading to unstable timing and high CPU usage. Aim for the minimum frequency required for smooth motion.
- Dangling Logic: If downstream nodes are not "exec-driven" (they don't have an input port for execution triggers), adding more
Tick roots will not affect their update frequency.
- Deterministic Drift: While the tick attempts to be consistent, actual execution time depends on the system load. Monitor the
dt (delta time) output if your logic requires millisecond-precise timing across different hardware.
Operator Reference
- Exec in ports: none
- Exec out ports:
exec
- Exec outputs:
exec
- Data inputs: none
- Data outputs:
processingMs, intervalMs, latenessMs
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
tickMs |
rw |
true |
true |
integer / default=100 |
Interval in milliseconds for emitting exec ticks. |
hiResTimer |
rw |
true |
false |
boolean / default=True |
Request 1ms system timer resolution to reduce jitter on Windows. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
tickMs (Tick (ms), rw): Interval in milliseconds for emitting exec ticks. Schema: integer / default=100.
hiResTimer (High-res Timer (Windows), rw): Request 1ms system timer resolution to reduce jitter on Windows. Schema: boolean / default=True.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
None
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
processingMs |
true |
false |
integer / default=0 |
Per-tick processing time in milliseconds (excluding sleep). |
intervalMs |
true |
false |
integer / default=0 |
Actual interval between tick starts in milliseconds. |
latenessMs |
true |
false |
integer / default=0 |
How late this tick started relative to its scheduled deadline (ms). |
Sequence (f8.exec_sequence)
Exec flow splitter: triggers its exec outputs in order (requires DFS scheduling).
When to Use
- Use
Sequence when one exec trigger needs to fan out into multiple branches in a fixed order.
- It is useful for making evaluation order explicit on the canvas.
- Put it near the top of a chain when later branches depend on work done by earlier branches in the same tick.
Common Wiring Patterns
- Ordered Pipeline: Feed a
Tick into Sequence, then reserve output 0 for reads, 1 for transforms, and 2 for side effects.
- Split Side Effects: Trigger logging or visualization on one branch before hardware output on a later branch.
- Startup Orchestration: Use separate outputs for reset, calibration, and normal execution steps.
Pitfalls / Gotchas
- Order Only:
Sequence controls execution order, not isolation; a slow early branch still delays later ones.
- Branch Sprawl: Overusing nested sequences can make graphs harder to read than separate subgraphs or service hosts.
- Port Priority: Lower-numbered exec outputs run first, so wire dependencies accordingly.
Operator Reference
- Exec in ports:
exec
- Exec out ports:
0, 1, 2
- Exec inputs:
exec
- Exec outputs:
0, 1, 2
- Data inputs: none
- Data outputs: none
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
None
Data Output Ports
None
- No bundled scenario references this node yet.
Cosine (f8.cosine)
Cosine phase transform. Provide phase (0..1) from an upstream phase driver (e.g. Phase node).
When to Use
- Use
Cosine to transform a normalized phase signal (usually 0 to 1) into a smooth, periodic waveform based on the cosine function.
- it is a fundamental building block for rhythmic motion, breathing patterns, or periodic modulation in your graph.
- Use it when you need a smooth, continuous oscillation that can be easily tuned for amplitude and DC offset.
Common Wiring Patterns
- Standard LFO: Feed the
phase input from an f8-phase or f8-tick operator. Send the mapping value into a Range Map or f8-viz-wave.
- Rhythmic Modulation: Use multiple
Cosine nodes with different frequencies to create complex, multi-layered "interference" patterns for more organic motion.
- Dynamic Tuning: Bind the
amp (amplitude) or dc (offset) properties to other control nodes to dynamically change the intensity or center point of the oscillation.
Pitfalls / Gotchas
- Phase Dependency: If the upstream phase source is jittery or incorrect, no amount of amplitude tuning will fix the resulting waveform. Verify your phase source first.
- Output Range: By default, a cosine wave moves between
-amp+dc and +amp+dc. Ensure your downstream nodes (like f8-tcode) are prepared for these values, or use a Range Map to normalize them.
- Phase Wraparound: If your phase source doesn't wrap cleanly at 1.0, the cosine wave will have a visible "jump" or discontinuity.
Operator Reference
- Exec in ports: none
- Exec out ports: none
- Data inputs:
phase, amp, dc, phaseOffset
- Data outputs:
value
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
dc |
rw |
true |
false |
number / default=0.5 |
Default DC offset (used when dc input is not provided). |
amp |
rw |
true |
false |
number / default=0.5 |
Amplitude. |
phaseOffset |
rw |
true |
false |
number / default=0.0 |
Normalized phase offset (0.0 to 1.0). |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
dc (DC, rw): Default DC offset (used when dc input is not provided). Schema: number / default=0.5.
amp (Amp, rw): Amplitude. Schema: number / default=0.5.
phaseOffset (Phase Offset, rw): Normalized phase offset (0.0 to 1.0). Schema: number / default=0.0.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
| Name |
Required |
On Node |
Schema |
Description |
phase |
true |
true |
number |
Phase input (0..1). |
amp |
false |
false |
number / default=0.5 |
Amplitude override. |
dc |
false |
false |
number / default=0.5 |
DC offset override. |
phaseOffset |
false |
false |
number |
Phase offset override (0..1). |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
value |
true |
true |
number |
cosine output |
Tempest (f8.tempest)
Tempest phase transform (phase-modulated cosine). Provide phase (0..1) from an upstream phase driver (e.g. Phase node).
When to Use
- Use
Tempest when you want a phase-driven, procedural waveform that has more "personality" and controllable asymmetry than a standard sine or cosine wave.
- It is ideal for motion patterns that require controllable curvature, "breathing" rhythms, or eccentric motion shapes (e.g., a fast stroke with a slow return).
- Best for creating organic, non-mechanical rhythmic oscillations in haptic or visual graphs.
Common Wiring Patterns
- Organic Oscillator: Feed the
phase input from f8-phase. Play with the eccentric and curve properties while watching the result on f8-viz-wave to find an interesting pulsing pattern.
- Dynamic Shaping: Map external signals (like audio energy) to the
amp or speed parameters to make the waveform grow and shrink in intensity based on the environment.
- Actuator Driver: Use the output value after a
Range Map to drive physical hardware with a more complex motion profile than a simple sine wave.
Pitfalls / Gotchas
- Complexity Overhead: If the
eccentric setting is too extreme, the resulting waveform can appear "broken" or glitchy if the upstream phase source is not perfectly stable.
- Normalization Requirements: Unlike
Cosine, Tempest can produce a wider variety of shape ranges. Always re-normalize the result with a Range Map before sending it to hardware.
- Design Intent: Use it as a specialized "shaping" tool. If you only need a simple, predictable rhythmic wave,
f8-cosine is often easier to tune and reason about.
Operator Reference
- Exec in ports: none
- Exec out ports: none
- Data inputs:
phase, amp, phaseOffset, eccentric, dc
- Data outputs:
out
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
dc |
rw |
true |
false |
number / default=0.5 |
Default DC offset (used when dc input is not provided). |
amp |
rw |
true |
false |
number / default=0.5 |
Default amplitude (used when amp/amplitude input is not provided). |
phaseOffset |
rw |
true |
false |
number / default=0.0 |
Fraction of a full cycle added to the phase (0..1). |
eccentric |
rw |
true |
false |
number / default=0.0 |
Controls curvature of the inner sine. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
dc (DC, rw): Default DC offset (used when dc input is not provided). Schema: number / default=0.5.
amp (Amp, rw): Default amplitude (used when amp/amplitude input is not provided). Schema: number / default=0.5.
phaseOffset (Phase Offset, rw): Fraction of a full cycle added to the phase (0..1). Schema: number / default=0.0.
eccentric (Eccentric, rw): Controls curvature of the inner sine. Schema: number / default=0.0.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
| Name |
Required |
On Node |
Schema |
Description |
phase |
true |
true |
number |
Phase input (0..1). |
amp |
false |
false |
number |
Amplitude override. |
phaseOffset |
false |
false |
number |
Phase offset override (0..1). |
eccentric |
false |
false |
number |
Eccentricity override |
dc |
false |
false |
number |
DC offset override. |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
out |
true |
true |
number |
tempest output |
Phase (f8.phase)
Phase accumulator. Outputs normalized phase (0..1) and unwrapped phase turns.
When to Use
- Use
Phase when your graph needs a normalized oscillator (0 to 1) or a continuous cycle counter to drive periodic events.
- It is the standard "clock" for the Feel8 graph, serving as the authoritative driver for
Cosine, Wave Pattern, and many rhythmic modulation chains.
- Use it to synchronize multiple independent operators to the same temporal heartbeat.
Common Wiring Patterns
- Master Clock: Feed the
phase output into a variety of waveform generators (Cosine, Tempest). Use phaseTurns for logging or sync branches that need to know how many full cycles have elapsed.
- Interactive Tempo: Connect the
hz (frequency) input to an external control (like a slider or an audio BPM analyzer) to make your motion react to the environment in real-time.
- Triggered Reset: Use the
reset input or command to restart the oscillator from 0 when a specific event occurs (e.g., a new track starts).
Pitfalls / Gotchas
- Redundant Clocks: If your graph already has an authoritative timebase (e.g., from a video player or an external MIDI clock), adding a secondary
Phase node can make the system behavior difficult to synchronize and reason about.
- Visual Validation: Always verify the phase reset and wrap behavior with an
f8-viz-wave node before connecting it to physical hardware to avoid sudden mechanical jerks.
- Hz Resolution: Very high frequencies might produce aliasing-like effects if the graph's overall update rate is too low.
Operator Reference
- Exec in ports: none
- Exec out ports: none
- Data inputs:
hz, phase, reset
- Data outputs:
phase, phaseTurns
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
hz |
rw |
true |
true |
number / default=1.0 |
Frequency in Hz. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
hz (Hz, rw): Frequency in Hz. Schema: number / default=1.0.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
| Name |
Required |
On Node |
Schema |
Description |
hz |
false |
true |
number |
Frequency override (Hz). |
phase |
false |
true |
number |
Absolute phase override (0..1). |
reset |
false |
true |
boolean |
If true, reset phase to 0. |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
phase |
true |
true |
number |
Normalized phase (0..1). |
phaseTurns |
true |
true |
number |
Unwrapped phase turns (cycles). |
Print (f8.print)
Exec-driven printer (pulls value and prints).
When to Use
- Use
Print when you need a quick, execution-driven (exec) diagnostic sink to output values or messages to the Studio console or engine logs.
- It is a development-time tool for verifying that specific logic branches are actually firing and for inspecting variable values in real-time.
- Not intended for production user interfaces; use
f8-viz-text for persistent on-canvas monitoring.
Common Wiring Patterns
- Branch Verification: Trigger it from a
Sequence or State Trigger only on the specific branch you are diagnosing. This ensures the log message reflects the correct execution context.
- Value Inspection: Connect the input to any data port (scalar, string, or complex JSON) to see its contents at the exact moment of execution.
- Trigger Logging: Use it to confirm that "one-shot" events (e.g., successful calibration, sequence done) have occurred without needing to watch a visualizer constantly.
Pitfalls / Gotchas
- Log Spam: Leaving too many
Print nodes active in a high-frequency loop (like a Tick node) will flood the console, potentially hiding more important system logs and slightly impacting performance.
- Sink Behavior:
Print is a terminal "sink." If you need to pass the data further down the graph for other operators to use, ensure you branch the signal before it reaches the print node.
- Ordering: Remember that messages will appear in the order they are executed in the engine thread, which may differ from their spatial arrangement on the Studio canvas.
Operator Reference
- Exec in ports:
exec
- Exec out ports: none
- Exec inputs:
exec
- Data inputs:
value
- Data outputs: none
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
strip |
wo |
true |
false |
boolean / default=True |
If true, strip whitespace/newlines from the start/end of string values before printing. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
strip (Strip, wo): If true, strip whitespace/newlines from the start/end of string values before printing. Schema: boolean / default=True.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
| Name |
Required |
On Node |
Schema |
Description |
value |
true |
true |
any |
value to print |
Data Output Ports
None
- No bundled scenario references this node yet.
Program Wave (f8.program_wave)
Generate a program-controlled phase/gate waveform from a dict state payload.
When to Use
- Use
Program Wave when your graph needs to emit or shape a structured "wave/program" payload (a collection of points or parameters) rather than just a single scalar value.
- It is essential for authoring complex motion sequences or "scripts" upstream of playback nodes or device-specific formatters.
- Use it when you want to group multiple motion parameters into a single reusable "program" that can be shared across multiple output channels.
Common Wiring Patterns
- Motion Sequencer: Pair it with a
Tick or Phase source. Use the output to drive a Sequence Player or a protocol-specific node like f8-tcode.
- Global Modulation: Keep the
Program Wave node upstream from individual device nodes so that one complex motion pattern can drive multiple hardware targets in sync.
- Payload Inspection: Always route a branch through
f8-viz-text to verify that the generated point data matches the expected schema for your target protocol.
Pitfalls / Gotchas
- Schema Strictness: Program-oriented nodes depend on a very specific internal payload structure. Any mismatch in port names or data types will cause downstream nodes to ignore the data silently.
- Abstraction Overload: If your graph only needs to move a single actuator between two points, a
Program Wave might be more complex than necessary. For simple cases, sticks to f8-cosine or f8-range-map.
- Frequency Matching: Ensure the update rate of your program is high enough to avoid stuttering on the physical device, but low enough common bandwidth limits of serial or network protocols.
Operator Reference
- Exec in ports: none
- Exec out ports: none
- Data inputs: none
- Data outputs:
phaseTurns, phase, active, done, elapsedSec
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
program |
wo |
true |
true |
object{hz, loopPauseSec, loopRunningSec, timeSec, ...} |
Dict payload defining tsMs/timeSec/hz/loopRunningSec/loopPauseSec. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
program (Program, wo): Dict payload defining tsMs/timeSec/hz/loopRunningSec/loopPauseSec. Schema: object{hz, loopPauseSec, loopRunningSec, timeSec, ...}.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
None
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
phaseTurns |
true |
true |
number |
Unwrapped phase turns (cycles). |
phase |
true |
true |
number |
Normalized phase (0..1). |
active |
true |
true |
boolean |
Whether program is in a running window. |
done |
true |
true |
boolean |
Whether program finished (timeSec elapsed). |
elapsedSec |
true |
true |
number |
Elapsed seconds since start. |
- No bundled scenario references this node yet.
Envelope (f8.envelope)
Track a signal envelope and normalize it into a stable 0..1 range.
Core
- Input value is tracked with lower and upper envelope estimators.
- Outputs are lower, upper, and normalized.
- normalized maps the current input between the tracked envelopes, including optional margin and minimum span.
Envelope Modes
- EMA: simple exponential tracking
- DEMA: double exponential tracking with faster response
- SMA: moving average window smoothing
Jump Handling
- Optional jump detection can reseed the envelopes after large sustained changes.
- Jump settings control the trigger threshold, consecutive frames, and reseed blend time.
Examples
- Normalize a noisy control signal into normalized
- Track changing lower/upper motion bounds
When to Use
- Use
Envelope when you want to convert a noisy, high-frequency, or fast-changing scalar signal into a smoother "magnitude trace" or follower.
- It is most commonly used in audio-reactive graphs (following loudness) and gesture-reactive graphs (following motion intensity).
- Use it to extract the general "energy" of a signal while ignoring its individual peaks and valleys.
Common Wiring Patterns
- Energy Follower: Feed it from a feature output (like audio loudness). Send the resulting envelope into a
Smooth Filter or Range Map to drive motion.
- Visual Saliency: Use the envelope of a motion signal to drive the transition of a classifier's weight, making the system more sensitive when more motion is detected.
- Sequence Tuning: Place it before downstream scaling so that later nodes in the chain receive a stable, predictable signal range.
Pitfalls / Gotchas
- Latency vs. Smoothness: Over-smoothing the envelope (long attack/decay) can make a graph feel "heavy" or laggy, even if the source data is accurate. Tune the attack and release times carefully.
- Input Quality: If the input source is fundamentally wrong, empty, or entirely noise, no amount of envelope tuning will produce a meaningful control signal. Validate your source with
f8-viz-wave first.
- Range Assumptions: Envelopes often produce values in a different range than the input. Use a
Range Map immediately after the envelope to bring it into a standard 0-1 control space.
Operator Reference
- Exec in ports: none
- Exec out ports: none
- Data inputs:
value
- Data outputs:
lower, upper, normalized
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
method |
rw |
true |
true |
string / enum[EMA, DEMA, SMA] / default=EMA |
Envelope tracking method: EMA, DEMA, or SMA. |
rise_alpha |
rw |
true |
true |
number / default=0.4 |
Smoothing factor when the estimator moves toward the current envelope edge. |
fall_alpha |
rw |
true |
true |
number / default=0.05 |
Smoothing factor when the estimator relaxes away from the current envelope edge. |
min_span |
rw |
true |
true |
number / default=0.25 |
Minimum enforced distance between lower and upper envelopes before normalization. |
sma_window |
rw |
true |
true |
number / default=10 |
Moving-average window size used when Method is SMA. |
margin |
rw |
true |
false |
number / default=0.0 |
Extra padding added outside the envelopes before computing normalized. |
jumpEnabled |
rw |
true |
false |
boolean / default=True |
Enable consecutive-frame jump detection and reseed. |
jumpSpanMult |
rw |
true |
false |
number / default=4.0 |
Distance threshold in envelope-span units for jump detection. |
jumpConsecutiveFrames |
rw |
true |
false |
number / default=4 |
Consecutive far frames required before jump trigger. |
jumpReseedFrames |
rw |
true |
false |
number / default=8 |
Blend length (frames) after jump reset. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
method (Method, rw): Envelope tracking method: EMA, DEMA, or SMA. Schema: string / enum[EMA, DEMA, SMA] / default=EMA.
rise_alpha (Rise Alpha, rw): Smoothing factor when the estimator moves toward the current envelope edge. Schema: number / default=0.4.
fall_alpha (Fall Alpha, rw): Smoothing factor when the estimator relaxes away from the current envelope edge. Schema: number / default=0.05.
min_span (Min Span, rw): Minimum enforced distance between lower and upper envelopes before normalization. Schema: number / default=0.25.
sma_window (SMA Window, rw): Moving-average window size used when Method is SMA. Schema: number / default=10.
margin (Margin, rw): Extra padding added outside the envelopes before computing normalized. Schema: number / default=0.0.
jumpEnabled (Jump Enabled, rw): Enable consecutive-frame jump detection and reseed. Schema: boolean / default=True.
jumpSpanMult (Jump Span Mult, rw): Distance threshold in envelope-span units for jump detection. Schema: number / default=4.0.
| Name |
Required |
On Node |
Schema |
Description |
value |
false |
true |
number |
Input value. |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
lower |
true |
true |
number |
Estimated lower envelope. |
upper |
true |
true |
number |
Estimated upper envelope. |
normalized |
true |
true |
number |
Normalized value (0..1). |
Smooth Filter (f8.smooth_filter)
Smooths scalar or vector inputs with EMA/DEMA/One Euro filtering.
When to Use
- Use
Smooth Filter when a numeric signal already exists but contains jitter, noise, or sudden spikes that need temporal stabilization.
- It is a essential second-stage "cleanup" node after feature extraction (audio) or coarse coordinate mapping (vision/pose).
- Best for creating smooth, organic motion from noisy real-world data sources.
Common Wiring Patterns
- Cleanup Pipeline: Place it after an
Envelope or Range Map operator. Compare the raw vs. filtered signal in parallel on f8-viz-wave to find the ideal smoothing coefficient.
- Actuator Guard: Place a filter immediately before high-frequency device outputs to prevent "chatter" and reduce mechanical wear on actuators.
- Signal Separation: Use different filter settings for different semantic signals (e.g., a "slow" filter for average loudness and a "fast" filter for onset tracking).
Pitfalls / Gotchas
- Responsiveness Tradeoff: Higher smoothing values increase stability but introduce noticeable latency (lag). Always tune the filter while interacting with the system to find the "sweet spot" for your use case.
- Input Error Hiding: A heavy filter can hide systemic instability or logic errors in the upstream path. Always validate your raw signal before applying aggressive smoothing.
- Initialization Jumps: The filter may produce a large "jump" output when the first data point arrives if the internal state isn't reset properly.
Operator Reference
- Exec in ports: none
- Exec out ports: none
- Data inputs:
value
- Data outputs:
value
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
filter_type |
rw |
true |
true |
string / enum[NONE, EMA, DEMA, ONEEURO] / default=EMA |
Filter type. |
ema_alpha |
rw |
true |
true |
number / default=0.4 |
EMA smoothing factor (0..1). |
dema_alpha |
rw |
true |
false |
number / default=0.4 |
DEMA smoothing factor (0..1). |
one_euro_min_cutoff |
rw |
true |
false |
number / default=1.5 |
Minimum cutoff frequency. |
one_euro_beta |
rw |
true |
false |
number / default=0.0 |
Speed coefficient for dynamic cutoff. |
one_euro_derivative_cutoff |
rw |
true |
false |
number / default=1.0 |
Cutoff frequency for the derivative filter. |
one_euro_default_freq |
rw |
true |
false |
number / default=90.0 |
Default sampling frequency (Hz). |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
filter_type (Filter, rw): Filter type. Schema: string / enum[NONE, EMA, DEMA, ONEEURO] / default=EMA.
ema_alpha (EMA Alpha, rw): EMA smoothing factor (0..1). Schema: number / default=0.4.
dema_alpha (DEMA Alpha, rw): DEMA smoothing factor (0..1). Schema: number / default=0.4.
one_euro_min_cutoff (One Euro Min Cutoff, rw): Minimum cutoff frequency. Schema: number / default=1.5.
one_euro_beta (One Euro Beta, rw): Speed coefficient for dynamic cutoff. Schema: number / default=0.0.
one_euro_derivative_cutoff (One Euro Derivative Cutoff, rw): Cutoff frequency for the derivative filter. Schema: number / default=1.0.
one_euro_default_freq (One Euro Default Freq, rw): Default sampling frequency (Hz). Schema: number / default=90.0.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
| Name |
Required |
On Node |
Schema |
Description |
value |
true |
true |
any |
Value to filter. |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
value |
true |
true |
any |
Filtered output. |
Range Map (f8.range_map)
Clip input to [inMin,inMax] then remap to [outMin,outMax] with a curve.
When to Use
- Use
Range Map when one numeric range (e.g., 0-100) must be clipped and remapped into another (e.g., 0-1).
- It is the default scaling node for preparing any signal before it reaches actuator-facing outputs or visualizations.
- Use it to convert raw sensor data, detection scores, or feature magnitudes into normalized control values.
Common Wiring Patterns
- Signal Normalization: Feed it cleaned scalar values from a detector or audio feature node, then send the mapped output to
f8-tcode, f8-serial-out, or f8-viz-wave.
- Calibration Loop: Tune
inMin and inMax against real-time observed source values (using f8-viz-wave for reference) before finalizing the outMin and outMax for your hardware.
- Curve Shapping: Use the
exponent property to apply non-linear curves (e.g., exponential growth for more sensitivity at lower values).
Pitfalls / Gotchas
- Input Calibration: A bad or uncalibrated input range (
inMin/inMax) is the most frequent cause of "dead" actuators or clipping. Verify your source signal range first.
- Output Clipping: Out-of-bounds input values are clipped to
outMin/outMax by default. ensure your mapping account for the full expected range of the signal.
- Semantic Clarity: Map your values into a 0-1 range early in the graph to maintain a consistent "normalized" signal flow, moving the device-specific scaling to the very end of the chain.
Operator Reference
- Exec in ports: none
- Exec out ports: none
- Data inputs:
value
- Data outputs:
value
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
inMin |
rw |
true |
false |
number / default=0.0 |
Input range minimum. |
inMax |
rw |
true |
false |
number / default=1.0 |
Input range maximum. |
outMin |
rw |
true |
true |
number / default=0.0 |
Output range minimum. |
outMax |
rw |
true |
true |
number / default=1.0 |
Output range maximum. |
curve |
rw |
true |
true |
string / enum[LINEAR, SMOOTHSTEP, SMOOTHERSTEP, EASE_IN, EASE_OUT, ...] / default=LINEAR |
Mapping curve. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
inMin (Input Min, rw): Input range minimum. Schema: number / default=0.0.
inMax (Input Max, rw): Input range maximum. Schema: number / default=1.0.
outMin (Output Min, rw): Output range minimum. Schema: number / default=0.0.
outMax (Output Max, rw): Output range maximum. Schema: number / default=1.0.
curve (Curve, rw): Mapping curve. Schema: string / enum[LINEAR, SMOOTHSTEP, SMOOTHERSTEP, EASE_IN, EASE_OUT, ...] / default=LINEAR.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
| Name |
Required |
On Node |
Schema |
Description |
value |
false |
true |
number |
Input value. |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
value |
true |
true |
number |
Mapped output. |
Rate Limiter (f8.rate_limiter)
Limits the rate of change (and optionally acceleration) of an input signal.
When to Use
- Use
Rate Limiter when you need to ensure that an output value changes no faster than a specific "safe" rate (slope limiting) or doesn't exceed a maximum step size.
- It is a critical "safety node" for protecting physical actuators and hardware from sudden, violent movements caused by noise or upstream logic jumps.
- Best for smoothing out "teleporting" values from detectors or trackers that occasionally lose their lock.
Common Wiring Patterns
- Safety Guard: Place it late in the control chain, after all mapping, smoothing, and logic but immediately before the hardware output node (e.g.,
Lovense Out, Serial Out).
- Visual Validation: Use
f8-viz-wave to compare the "Unlimited" vs. "Limited" signals side-by-side to ensure the limits are safe for your specific hardware while remaining responsive enough for the user.
- Actuator Lifetime: Use conservative limits during general development to reduce mechanical wear on your devices, then widen them for "high-dynamic" scenarios when necessary.
Pitfalls / Gotchas
- Placement Order: If placed too early in the graph, the rate limiter can distort the logic of subsequent operators (like Envelopes or Smooth Filters) that expect to see raw signal dynamics.
- Responsiveness Lag: Aggressive rate limiting will make your graph feel sluggish or "unresponsive," even if the upstream vision/audio detection is frame-perfect. Always tune this while physically testing the system.
- Step vs Slope: Understand the difference between limiting the total change per second (slope) and the maximum allowed change in a single frame (step). Misconfiguring these can lead to unexpected "damping" effect.
Operator Reference
- Exec in ports: none
- Exec out ports: none
- Data inputs:
value
- Data outputs:
value
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
inMin |
rw |
true |
false |
number / default=0.0 |
Input/output clamp minimum (typical 0). |
inMax |
rw |
true |
false |
number / default=1.0 |
Input/output clamp maximum (typical 1). |
maxRateUp |
rw |
true |
true |
number / default=2.0 |
Maximum rising rate (units/sec). |
maxRateDown |
rw |
true |
true |
number / default=2.0 |
Maximum falling rate (units/sec). |
maxAccel |
rw |
true |
false |
number / default=0.0 |
Maximum acceleration (units/sec^2). 0 disables acceleration limiting. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
inMin (Input Min, rw): Input/output clamp minimum (typical 0). Schema: number / default=0.0.
inMax (Input Max, rw): Input/output clamp maximum (typical 1). Schema: number / default=1.0.
maxRateUp (Max Rate Up, rw): Maximum rising rate (units/sec). Schema: number / default=2.0.
maxRateDown (Max Rate Down, rw): Maximum falling rate (units/sec). Schema: number / default=2.0.
maxAccel (Max Accel, rw): Maximum acceleration (units/sec^2). 0 disables acceleration limiting. Schema: number / default=0.0.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
| Name |
Required |
On Node |
Schema |
Description |
value |
false |
true |
number |
Input value. |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
value |
true |
true |
number |
Rate-limited output. |
Serial Out (f8.serial_out)
Writes incoming values to a serial port (pyserial).
When to Use
- Use
Serial Out as the final hardware sink for sending TCode or other text-based command streams to a device connected via a COM port (USB-Serial, Arduino, ESP32).
- It is the most common path for controlling DIY machines (like the OSR2 or SR6) and custom embedded hardware.
- Use it when you need low-latency, direct-to-metal communication without an intermediate network or Bluetooth layer.
Common Wiring Patterns
- Hardware Command Loop: Feed it from an
f8-tcode operator or any other finalized string-producing node. Always keep a TCodeViz node in parallel during initial hardware bring-up to see exactly what is being sent.
- Safety Chains: Ensure all safety limits, smoothing, and range mapping are handled upstream in the graph. The serial node should strictly be a "dumb" transport layer.
- Port Setup: Select the correct COM port and baud rate in the node properties. It is recommended to use 115200 or higher for smooth haptic feedback.
Pitfalls / Gotchas
- Port Access Conflicts: The most common error is trying to open a port that is already in use by another application (e.g., Arduino IDE, another Studio instance). Verify the port is free before starting the scenario.
- Baud Rate Mismatch: If the baud rate on the node doesn't match the firmware on your device, you will see garbage characters or the device will not react at all.
- Format Errors: Validate the outgoing string format in the editor before blaming the hardware. Missing a newline character or a semicolon at the end of a command is a frequent cause of "unresponsive" hardware.
Operator Reference
- Exec in ports:
exec
- Exec out ports: none
- Exec inputs:
exec
- Data inputs:
value
- Data outputs:
isOpen, writtenBytes, error
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
enabled |
rw |
true |
true |
boolean / default=True |
Enable/disable serial output. |
port |
rw |
true |
true |
string / default=COM4 |
Serial port name (e.g., COM3). |
baudrate |
wo |
true |
false |
integer / default=115200 |
Serial baud rate. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
enabled (Enabled, rw): Enable/disable serial output. Schema: boolean / default=True.
port (Port, rw): Serial port name (e.g., COM3). Schema: string / default=COM4.
baudrate (Baudrate, wo): Serial baud rate. Schema: integer / default=115200.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
| Name |
Required |
On Node |
Schema |
Description |
value |
true |
true |
any |
Value to write. |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
isOpen |
true |
true |
boolean / default=False |
Whether serial port is open. |
writtenBytes |
true |
true |
integer / default=0 |
Bytes written by last exec. |
error |
true |
true |
string / default= |
Last error (if any). |
UDP In (f8.udp_in)
Receives UDP packets and exposes explicit raw/text/json views plus packet metadata.
When to Use
- Use
UDP In as the generic ingress node for any UDP packet stream.
- It is the right starting point when the payload format is not yet decoded, or when multiple downstream consumers need access to the same packet metadata.
- Pair it with
Skeleton Decoder or VMC Decoder for motion protocols instead of using legacy protocol-specific UDP nodes.
Common Wiring Patterns
- Binary Motion Stream: Connect
packet to Skeleton Decoder.packet or VMC Decoder.packet so downstream nodes receive the raw payload plus packet metadata.
- JSON Packet Ingest: Wire
json into downstream logic when the sender emits UTF-8 JSON payloads, and keep text attached for inspection.
- Debug Branch: Attach
Text Viz or Print to text, json, or packet while keeping protocol decoding on a separate branch.
Pitfalls / Gotchas
- Exact Bytes vs Packet Envelope: Use
raw when downstream only needs payload bytes. Use packet when downstream also needs source/timestamp metadata or exec-context packet snapshots.
- No Packet-Rate State: Packet counters, byte lengths, remote address, and parse diagnostics are intentionally not published as state. Read packet-rate information from
raw, text, json, or packet outputs so the Studio UI state sync does not get flooded.
- Bind Security: Non-loopback bind addresses stay blocked unless
allowNonLoopbackBind is enabled explicitly.
- Protocol Split:
UDP In does not decode motion payloads on its own anymore; decoding must happen in a dedicated downstream operator.
Operator Reference
- Exec in ports: none
- Exec out ports:
packet
- Exec outputs:
packet
- Data inputs: none
- Data outputs:
text, raw, json, packet
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
bindAddress |
rw |
true |
false |
string / default=127.0.0.1 |
Local address to bind (loopback by default). |
allowNonLoopbackBind |
rw |
true |
false |
boolean / default=False |
When true, allow bindAddress values other than loopback. |
port |
rw |
true |
true |
integer / default=39541 |
UDP listen port. |
maxQueue |
rw |
true |
false |
integer / default=512 |
Max queued packets before dropping (1..4096). |
reuseAddress |
rw |
true |
false |
boolean / default=False |
Best-effort: allow multiple listeners on the same bind tuple if the OS supports it. |
listening |
ro |
true |
true |
boolean / default=False |
Readonly flag telling whether the UDP socket is active. |
lastError |
ro |
true |
true |
string / default= |
Readonly receiver/socket error, updated only when the error state changes. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
bindAddress (Bind Address, rw): Local address to bind (loopback by default). Schema: string / default=127.0.0.1.
allowNonLoopbackBind (Allow Non-loopback Bind, rw): When true, allow bindAddress values other than loopback. Schema: boolean / default=False.
port (Port, rw): UDP listen port. Schema: integer / default=39541.
maxQueue (Max Queue, rw): Max queued packets before dropping (1..4096). Schema: integer / default=512.
reuseAddress (Reuse Address, rw): Best-effort: allow multiple listeners on the same bind tuple if the OS supports it. Schema: boolean / default=False.
listening (Listening, ro): Readonly flag telling whether the UDP socket is active. Schema: boolean / default=False.
lastError (Last Error, ro): Readonly receiver/socket error, updated only when the error state changes. Schema: string / default=.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
None
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
text |
true |
true |
string / default= |
Latest packet decoded as UTF-8 text with replacement for invalid bytes. |
raw |
true |
true |
any |
Latest packet as bytearray, preserving non-ASCII bytes. |
json |
true |
true |
any |
Latest packet parsed as JSON when valid; otherwise None. |
packet |
true |
true |
object{byteLength, json, jsonValid, raw, ...} |
Latest packet metadata plus raw/text/json views. |
Skeleton Decoder (f8.skeleton_decoder)
Decodes udp_in packet payloads into skeleton streams with chunk reassembly.
When to Use
- Use
Skeleton Decoder after UDP In when the incoming packets contain Feel8 skeleton payloads or chunked skeleton frames.
- It keeps the transport layer separate from payload decoding, which makes the graph easier to test and easier to swap to other packet sources later.
- Use it as the payload decoder in a
UDP In -> Skeleton Decoder chain for skeleton streams.
Common Wiring Patterns
- Latest Skeleton Stream: Connect
UDP In.packet to Skeleton Decoder.packet, then feed selectedSkeleton into Bone Selector, Bone Filter, or visualizers.
- Multi-Character Monitor: Use
skeletons to drive inspection tools that need the full active set, while selectedSkeleton drives the main control chain.
- Cached Pull Loop: Keep
UDP In.packet -> Skeleton Decoder.packet wired at packet rate, then drive a downstream solver or visualizer from a slower Tick.exec and pull skeletons or selectedSkeleton on demand.
- Chunk Reassembly: Keep this decoder close to the packet source so fragmented skeleton frames are reassembled before other operators consume them.
Pitfalls / Gotchas
- Transport Assumption: This node expects a packet object from
UDP In.packet; it is not meant to parse arbitrary text or JSON payloads directly.
- Latest-State Sampling: If a downstream node is driven by
Tick.exec and only pulls skeletons or selectedSkeleton, it samples the latest decoded state at that tick. Intermediate UDP packets are coalesced rather than replayed one-by-one.
- Selection Confusion:
selectedKey only works when the incoming model key exists in availableKeys; inspect that list first when nothing appears downstream.
- Packet Contract: Keep the upstream node on
UDP In.packet; this decoder expects the packet object rather than ad-hoc payload fragments.
Operator Reference
- Exec in ports:
packet
- Exec out ports:
packet
- Exec inputs:
packet
- Exec outputs:
packet
- Data inputs:
packet
- Data outputs:
skeletons, selectedSkeleton
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
cleanupAfterMs |
rw |
true |
false |
integer / default=10000 |
Remove models that haven't updated for this many ms (<=0 disables cleanup). |
selectedKey |
rw |
true |
true |
string / default= |
If set and matches an available key, outputs selectedSkeleton; otherwise None. |
availableKeys |
ro |
true |
true |
array[string] |
Read-only list of current keys (updated only on changes). |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
cleanupAfterMs (Cleanup After (ms), rw): Remove models that haven't updated for this many ms (<=0 disables cleanup). Schema: integer / default=10000.
selectedKey (Selected Key, rw): If set and matches an available key, outputs selectedSkeleton; otherwise None. Schema: string / default=.
availableKeys (Available Keys, ro): Read-only list of current keys (updated only on changes). Schema: array[string].
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
| Name |
Required |
On Node |
Schema |
Description |
packet |
true |
true |
any |
Packet payload from udp_in.packet. |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
skeletons |
true |
true |
array[object] |
List of latest payloads (ordered by key). |
selectedSkeleton |
true |
true |
object{boneCount, bones, modelName, schema, ...} |
Latest payload matching selectedKey (or None). |
UDP Out (f8.udp_out)
Sends incoming values to a UDP host/port.
When to Use
- Use
UDP Out to send values from a PyEngine graph to an external UDP listener.
- It is a simple bridge for diagnostics, remote control payloads, or interoperability with other local tools.
- This node is appropriate when delivery can be best-effort and connectionless.
Common Wiring Patterns
- Debug Egress: Send intermediate values to a small local receiver for inspection outside Studio.
- Device Bridge: Feed mapped values or formatted text from
Python Script, Data Expr, or protocol builders into UDP Out.value.
- Line-Based Text Sender: Enable
appendNewline or forceText when the receiver expects plain text records.
Pitfalls / Gotchas
- No Delivery Guarantee: UDP can drop or reorder packets, so do not assume reliable transport.
- Formatting Mismatch: Confirm whether the receiver expects text bytes or a binary payload before toggling
forceText.
- Network Scope: Sending to non-loopback targets still needs the right firewall and host configuration on both sides.
Operator Reference
- Exec in ports:
exec
- Exec out ports: none
- Exec inputs:
exec
- Data inputs:
value
- Data outputs:
isOpen, sentBytes, error
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
enabled |
rw |
true |
true |
boolean / default=True |
Enable/disable UDP output. |
host |
rw |
true |
true |
string / default=127.0.0.1 |
Target UDP host name or IP. |
port |
rw |
true |
true |
integer / default=9000 |
Target UDP port. |
appendNewline |
rw |
true |
false |
boolean / default=False |
Append a trailing newline to stringified values before sending. |
forceText |
rw |
true |
false |
boolean / default=True |
When true, convert incoming values to text before sending. When false, only bytes and str are accepted. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
enabled (Enabled, rw): Enable/disable UDP output. Schema: boolean / default=True.
host (Host, rw): Target UDP host name or IP. Schema: string / default=127.0.0.1.
port (Port, rw): Target UDP port. Schema: integer / default=9000.
appendNewline (Append Newline, rw): Append a trailing newline to stringified values before sending. Schema: boolean / default=False.
forceText (Force Text, rw): When true, convert incoming values to text before sending. When false, only bytes and str are accepted. Schema: boolean / default=True.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
| Name |
Required |
On Node |
Schema |
Description |
value |
true |
true |
any |
Value to send. |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
isOpen |
true |
true |
boolean / default=False |
Whether the UDP socket is open. |
sentBytes |
true |
true |
integer / default=0 |
Bytes sent by last exec. |
error |
true |
true |
string / default= |
Last error (if any). |
- No bundled scenario references this node yet.
TCode (f8.tcode)
Generates TCode v0.3 command strings from normalized axis values.
When to Use
- Use
TCode to convert one or more normalized numeric control channels (0-1) into a standard TCode string stream (e.g., L0500, V0123).
- It is the essential formatting bridge between your abstract motion logic and the physical commands understood by most haptic devices.
- Use it to bundle multiple independent signals (Stroke, Vibrate, Roll, etc.) into a single, synchronized command packet.
Common Wiring Patterns
- Device Command Chain: Feed it mapped numeric signals from
Range Map. Branch the resulting tcode string output to f8-serial-out, f8-handy-out, or a TCodeViz for inspection.
- Synchronized Updates: Align the
intervalMs property with the tick rate of your graph (e.g., 20ms for 50Hz) to ensure smooth, jitter-free device movement.
- Multi-Channel Authoring: Use a single
TCode operator to manage all axes of a complex device (like an OSR2) by wiring each axis to a dedicated input port.
Pitfalls / Gotchas
- Channel Semantic Mismatch: If you wire a "Vibration" signal into a "Stroke" axis in the TCode node, the physical device will move incorrectly. Double-check your axis mappings (
L0, V0, R1, etc.).
- Visual Verification First: Always view the emitted TCode string in a
TCodeViz node before debugging serial ports or network connections. If the string looks wrong, the problem is upstream.
- Resolution Limits: TCode typically expects 4-digit precision (0-9999). Ensure your upstream values are not being clipped or rounded in a way that creates a "steppy" feel on the device.
Operator Reference
- Exec in ports: none
- Exec out ports: none
- Data inputs:
L0, L1, L2, R0, R1, R2, V0, V1, A0, A1, intervalMs
- Data outputs:
tcode
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
intervalMs |
rw |
true |
true |
number / default=20 |
Default interval appended as I### when intervalMs input is not provided. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
intervalMs (Interval (ms), rw): Default interval appended as I### when intervalMs input is not provided. Schema: number / default=20.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
| Name |
Required |
On Node |
Schema |
Description |
L0 |
true |
true |
number |
Axis L0 (0..1). |
L1 |
true |
false |
number |
Axis L1 (0..1). |
L2 |
true |
false |
number |
Axis L2 (0..1). |
R0 |
true |
false |
number |
Axis R0 (0..1). |
R1 |
true |
false |
number |
Axis R1 (0..1). |
R2 |
true |
false |
number |
Axis R2 (0..1). |
V0 |
true |
false |
number |
Axis V0 (0..1). |
V1 |
true |
false |
number |
Axis V1 (0..1). |
A0 |
true |
false |
number |
Axis A0 (0..1). |
A1 |
true |
false |
number |
Axis A1 (0..1). |
intervalMs |
true |
false |
number / default=20 |
Optional interval override in milliseconds (rounded, min 1). |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
tcode |
true |
true |
string |
TCode v0.3 command string |
Python Script (f8.python_script)
Execute Python code with onStart/onState/onMsg/onExec/onStop hooks.
When to Use
- Use the
Python Script operator when a specific part of your graph needs bespoke logic that should still execute inside the high-performance f8.pyengine environment.
- It is the primary "escape hatch" for complex signal processing, domain-specific algorithms, or orchestrating flow between multiple operators.
- Choose this when you need to maintain internal state across multiple data frames (e.g., counters, moving averages, or state machines).
Common Wiring Patterns
- Modular Logic: Keep the script narrow in scope. Instead of one massive script for the whole scene, use multiple script nodes each handling a single responsibility (e.g., "Hand Gesture Logic," "Sequence Orchestration").
- Inspection Layers: Surround your script node with
f8-viz-text and f8-viz-wave nodes so its internal inputs and outputs stay obvious during debugging.
- Dynamic Port Scaling: Define your custom input and output ports in the node properties to make the script's interface explicit on the graph canvas.
Pitfalls / Gotchas
- Maintenance Bottleneck: A script node can become a "black box" that hides too much logic from the visual graph representation. Always document your code and keep the script's external interface clear.
- Port Naming: Avoid using generic names like
input1 or output1. Use semantic names (e.g., target_velocity, is_active) to make the data flow readable to others.
- Blocking Calls: Never perform blocking I/O (like
time.sleep() or synchronous requests) inside the script's processing callback, as this will stall the entire PyEngine thread and lock up your graph.
- State Leaks: Be careful with persistent variables; ensure your script handles initialization and reset logic properly when the graph starts or stops.
Operator Reference
- Exec in ports:
exec
- Exec out ports:
exec
- Exec inputs:
exec
- Exec outputs:
exec
- Data inputs:
msg
- Data outputs:
out
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
code |
rw |
true |
false |
string / default=# Hooks template (uncomment what you need):<br># - onStart(ctx)<br># - onState(ctx, field, value, ts_ms=None)<br># - onMsg(ctx, inputs)<br># - onExec(ctx, exec_in, inputs)<br># - onStop(ctx)<br>#<br># Notes:<br># - If you define no hooks, the node is a no-op.<br># - ctx.locals is preserved between calls (script-local memory)<br># - ctx.exec_in is set only for exec-triggered calls<br># - ctx.states.<field> reads cached rw/ro/wo state snapshot<br># - example: ctx.states.foo / ctx.states.pose.x<br># - await ctx.read_state(field) # fresh runtime read<br># - ctx.states.get(field) # cached snapshot<br># - ctx.set_state(field, value)<br># - await ctx.set_state_async(field, value)<br># - onStart return values are ignored; use ctx.emit()/ctx.set_state().<br># - inputs binding mode is configured by stateinputMode:<br># - input_view (default): supports dot and mapping access<br># - raw_dict: plain dict only (faster for mapping-style high-frequency scripts)<br># - msgspec_struct: typed struct from dataIn schema (faster for dot-style high-frequency scripts)<br># - State TypeGuard helpers are available from f8_dynamic_states<br># - example: from f8_dynamic_states import is_state_lastError<br># - then: if is_state_lastError(value, field): ...<br># - Video SHM helpers:<br># - ctx.subscribe_video_shm(key, shm_name, decode='auto', use_event=False)<br># - pkt = ctx.get_video_shm(key)<br># - ctx.unsubscribe_video_shm(key)<br># - ctx.list_video_shm_subscriptions()<br>#<br># Return value protocol:<br># - onMsg: {'outputs': {...}} or any value (emits to 'out' if present)<br># - onExec: {'exec': ['exec', ...], 'outputs': {...}}<br><br>from typing import TYPE_CHECKING, Any<br>if TYPE_CHECKING:<br> from f8_script_api import F8Inputs, F8PyEngineContext, F8States<br><br>def onStart(ctx: 'F8PyEngineContext') -> None:<br> ctx.log('python_script started')<br><br># def onState(<br># ctx: 'F8PyEngineContext',<br># field: str,<br># value: Any,<br># ts_ms: int \| None = None,<br># ) -> None:<br># ctx.log(f'state {field}={value} ts_ms={ts_ms}')<br>#<br># def onMsg(ctx: 'F8PyEngineContext', inputs: 'F8Inputs') -> dict[str, Any]:<br># msg = inputs.msg<br># return {'outputs': {'out': msg}}<br>#<br># def onExec(ctx: 'F8PyEngineContext', exec_in: str, inputs: 'F8Inputs') -> dict[str, Any]:<br># if exec_in == 'exec2':<br># return {'exec': ['exec2'], 'outputs': {'out': inputs.msg}}<br># return {'exec': ['exec'], 'outputs': {'out': inputs.msg}}<br>#<br># def onStop(ctx: 'F8PyEngineContext') -> None:<br># ctx.log('python_script stopped')<br> |
Python source code optionally defining hooks: onStart/onState/onMsg/onExec/onStop. |
inputMode |
rw |
true |
false |
string / enum[input_view, raw_dict, msgspec_struct] / default=input_view |
Input binding mode: input_view | raw_dict | msgspec_struct. For high-frequency scripts, prefer raw_dict for mapping access or msgspec_struct for dot access. |
lastError |
wo |
true |
false |
string / default= |
Last script error (compile/runtime). |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
code (Code, rw): Python source code optionally defining hooks: onStart/onState/onMsg/onExec/onStop. Schema: `string / default=# Hooks template (uncomment what you need):
- onStart(ctx)
- onState(ctx, field, value, ts_ms=None)
- onStop(ctx)
Notes:
- If you define no hooks, the node is a no-op.
- ctx.locals is preserved between calls (script-local memory)
- ctx.exec_in is set only for exec-triggered calls
- ctx.states. reads cached rw/ro/wo state snapshot
- example: ctx.states.foo / ctx.states.pose.x
- await ctx.read_state(field) # fresh runtime read
- ctx.states.get(field) # cached snapshot
- ctx.set_state(field, value)
- await ctx.set_state_async(field, value)
- onStart return values are ignored; use ctx.emit()/ctx.set_state().
- raw_dict: plain dict only (faster for mapping-style high-frequency scripts)
- msgspec_struct: typed struct from dataIn schema (faster for dot-style high-frequency scripts)
- State TypeGuard helpers are available from f8_dynamic_states
- example: from f8_dynamic_states import is_state_lastError
- then: if is_state_lastError(value, field): ...
- Video SHM helpers:
- ctx.subscribe_video_shm(key, shm_name, decode='auto', use_event=False)
- pkt = ctx.get_video_shm(key)
- ctx.unsubscribe_video_shm(key)
- ctx.list_video_shm_subscriptions()
Return value protocol:
- onMsg: {'outputs': {...}} or any value (emits to 'out' if present)
- onExec: {'exec': ['exec', ...], 'outputs': {...}}
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from f8_script_api import F8Inputs, F8PyEngineContext, F8States
def onStart(ctx: 'F8PyEngineContext') -> None:
ctx.log('python_script started')
def onState(
ctx: 'F8PyEngineContext',
field: str,
value: Any,
ts_ms: int | None = None,
) -> None:
ctx.log(f'state {field}={value} ts_ms={ts_ms}')
def onMsg(ctx: 'F8PyEngineContext', inputs: 'F8Inputs') -> dict[str, Any]:
return {'outputs': {'out': msg}}
def onExec(ctx: 'F8PyEngineContext', exec_in: str, inputs: 'F8Inputs') -> dict[str, Any]:
if exec_in == 'exec2':
def onStop(ctx: 'F8PyEngineContext') -> None:
ctx.log('python_script stopped')
.
-inputMode(Input Mode,rw): Input binding mode: input_view | raw_dict | msgspec_struct. For high-frequency scripts, prefer raw_dict for mapping access or msgspec_struct for dot access. Schema:string / enum[input_view, raw_dict, msgspec_struct] / default=input_view.
-lastError(Last Error,wo): Last script error (compile/runtime). Schema:string / default=.
-svcId(Service Id,ro): Readonly: current service instance id (svcId). Schema:string.
-operatorId(Operator Id,ro): Readonly: current operator/node id (operatorId). Schema:string`.
| Name |
Required |
On Node |
Schema |
Description |
msg |
false |
true |
any |
Message input |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
out |
false |
true |
any |
Script output |
Data Expr (f8.data_expr)
Evaluate a small Python expression using input values.
Core
- The main input is x, and any additional input port name can be referenced directly.
- The expression is a single Python expression, not a statement block.
- The result is emitted on out.
- If Unpack Dict Outputs is enabled and the result is a dict, matching output ports receive matching keys.
Available
- Builtins: abs, min, max, round, float, int, len, sum, sorted, range, any, all, sigmoid
- Python expressions: indexing, dict/list/tuple literals, comprehensions, conditionals
- Math namespace: math.*
- Optional numpy namespace: np.* and numpy.* when Allow Numpy is enabled
Examples
- x * 0.5
- max(0, x)
- {'left': x[0], 'right': x[1]}
- [value * 2 for value in x]
When to Use
- Use
Data Expr when you want to execute a compact Python expression over one or more input payloads without the overhead of a full Python Script node.
- It is a good fit for minor data transforms, field extraction from JSON, simple conditionals, or unpacking one result into multiple named outputs.
- Best for logic that is too complex for a single property but doesn't require complex state management.
Common Wiring Patterns
- Multi-Input Reduction: Feed it from multiple service or operator data ports. Reference the default input as
x or use named input ports directly in your expression.
- Output Unpacking: Enable
Unpack Dict Outputs when your expression returns a dictionary. The operator will automatically route values to output ports whose names match the dictionary keys.
- Signal Gating: Use a conditional expression (e.g.,
x if x > 0.5 else 0) to gate or filter incoming values before they move further down the graph.
Pitfalls / Gotchas
- Complexity Creep: Expressions stay maintainable only while they are small. Once you need persistent state, complex imports, or multi-step logic, you should upgrade the work to a
Python Script node.
- Port Mapping: Output unpacking only happens for keys that exactly match existing output port names. Double-check your spelling!
- Silent Failures: Expression errors are often swallowed or only visible in the engine logs. Use
f8-viz-text to monitor the output if your expression appears to be failing.
- Library Support: Optional
numpy support is available but must be explicitly enabled in node properties.
Operator Reference
- Exec in ports: none
- Exec out ports: none
- Data inputs:
x
- Data outputs:
out
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
allowNumpy |
rw |
false |
false |
boolean / default=False |
Enable np.* and numpy.* inside the expression. |
unpackDictOutputs |
rw |
false |
false |
boolean / default=False |
When enabled, dict results are unpacked into output ports with matching names. |
code |
rw |
false |
true |
string / default=x |
Single Python expression. Reference x and any extra input port names directly. Supports literals, indexing, comprehensions, conditionals, math.*, and optional np.* / numpy.* when Allow Numpy is enabled. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
allowNumpy (Allow Numpy, rw): Enable np.* and numpy.* inside the expression. Schema: boolean / default=False.
unpackDictOutputs (Unpack Dict Outputs, rw): When enabled, dict results are unpacked into output ports with matching names. Schema: boolean / default=False.
code (Expr, rw): Single Python expression. Reference x and any extra input port names directly. Supports literals, indexing, comprehensions, conditionals, math.*, and optional np.* / numpy.* when Allow Numpy is enabled. Schema: string / default=x.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
| Name |
Required |
On Node |
Schema |
Description |
x |
false |
true |
any |
Input value for the expression. |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
out |
false |
true |
any |
Expression result. |
- No bundled scenario references this node yet.
Lovense Out (f8.lovense_out)
Send Lovense Local API commands with split channels: sendPositionCmd->Position, sendFunctionCmd->Function.
When to Use
- Use
Lovense Out when your graph needs to control Lovense devices (e.g., Lush, Nora, Max) in real-time from the f8.pyengine runtime.
- It acts as the final device-facing sink, converting your graph signals into commands that are sent to the Lovense Connect app or a dedicated dongle.
- Choose this for high-precision control of vibration, rotation, or contraction intensity.
Common Wiring Patterns
- Direct Intensity Control: Feed it already-mapped control values (usually 0 to 20 for intensity) from an
f8-range-map.
- Logic Debugging: Keep the
Lovense Mock Server active during development to verify your graph logic without needing to wear or run the physical device.
- Multi-Device Support: Use multiple
Lovense Out nodes to target different devices independently within the same scenario.
Pitfalls / Gotchas
- Intensity Resolution: Lovense devices often have a limited number of "steps" (e.g., 0-20 or 0-100). Sending high-resolution floats (like 0.12345) will be rounded by the device transport layer, which can lead to a "steppy" feel if not handled carefully.
- Transport Latency: Communication via the Lovense Connect local API can introduce small delays. If the reaction feels laggy, reduce the command frequency or check your local network congestion.
- Connection persistence: Ensure the Lovense Connect app is running and your device is discovered before starting the Feel8 scenario, as the node will not automatically search for new devices once the graph is active.
Operator Reference
- Exec in ports:
sendPositionCmd, sendFunctionCmd
- Exec out ports: none
- Exec inputs:
sendPositionCmd, sendFunctionCmd
- Data inputs:
position
- Data outputs: none
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
enabled |
rw |
true |
true |
boolean / default=True |
Enable/disable Lovense output. |
commandUrl |
rw |
true |
false |
string / default=https://127-0-0-1.lovense.club:30010/command |
Lovense Local API /command URL. Reset to default when exporting publish JSON. |
platformName |
rw |
true |
false |
string / default=Feel8 Studio |
Value for X-platform request header. |
requestTimeoutMs |
rw |
true |
false |
integer / default=5000 |
HTTP timeout for Lovense requests. |
verifyTls |
rw |
true |
false |
boolean / default=True |
Verify HTTPS certificate when using https:// commandUrl. |
minSendIntervalMs |
rw |
true |
false |
integer / default=100 |
Minimum interval between Position commands sent by sendPositionCmd (0 disables throttling). |
vibrate |
rw |
true |
false |
number |
Normalized Function Vibrate level (0..1). |
rotate |
rw |
true |
false |
number |
Normalized Function Rotate level (0..1). |
pump |
rw |
true |
false |
number |
Normalized Function Pump level (0..1). |
thrusting |
rw |
true |
false |
number |
Normalized Function Thrusting level (0..1). |
fingering |
rw |
true |
false |
number |
Normalized Function Fingering level (0..1). |
suction |
rw |
true |
false |
number |
Normalized Function Suction level (0..1). |
depth |
rw |
true |
false |
number |
Normalized Function Depth level (0..1). |
oscillate |
rw |
true |
false |
number |
Normalized Function Oscillate level (0..1). |
all |
rw |
true |
false |
number |
Normalized Function All level (0..1). |
strokeMin |
rw |
true |
false |
number |
Normalized Function Stroke min (0..1). Requires strokeMax. |
strokeMax |
rw |
true |
false |
number |
Normalized Function Stroke max (0..1). Requires strokeMin. |
stop |
rw |
true |
false |
boolean / default=False |
When true, sendFunctionCmd sends Function Stop. |
timeSec |
rw |
true |
false |
number / default=0.0 |
Function timeSec. |
loopRunningSec |
rw |
true |
false |
number |
Optional Function loopRunningSec (omit when empty or <=0). |
loopPauseSec |
rw |
true |
false |
number |
Optional Function loopPauseSec (omit when empty or <=0). |
stopPrevious |
rw |
true |
false |
boolean / default=True |
Function stopPrevious (true->1, false->0). |
toy |
rw |
true |
true |
string / default= |
Optional target toy id. Empty uses defaultToy. |
defaultToy |
rw |
true |
false |
string / default= |
Fallback toy id when toy is empty. |
availableToys |
ro |
true |
false |
array[string] |
Discovered toy IDs from GetToys response. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
enabled (Enabled, rw): Enable/disable Lovense output. Schema: boolean / default=True.
commandUrl (Command URL, rw): Lovense Local API /command URL. Reset to default when exporting publish JSON. Schema: string / default=https://127-0-0-1.lovense.club:30010/command.
platformName (Platform Name, rw): Value for X-platform request header. Schema: string / default=Feel8 Studio.
requestTimeoutMs (Request Timeout (ms), rw): HTTP timeout for Lovense requests. Schema: integer / default=5000.
verifyTls (Verify TLS, rw): Verify HTTPS certificate when using https:// commandUrl. Schema: boolean / default=True.
minSendIntervalMs (Min Send Interval (ms), rw): Minimum interval between Position commands sent by sendPositionCmd (0 disables throttling). Schema: integer / default=100.
vibrate (Vibrate, rw): Normalized Function Vibrate level (0..1). Schema: number.
rotate (Rotate, rw): Normalized Function Rotate level (0..1). Schema: number.
| Name |
Required |
On Node |
Schema |
Description |
position |
true |
true |
number |
Normalized position input (0..1). Sent on sendPositionCmd as Lovense Position command. |
Data Output Ports
None
- No bundled scenario references this node yet.
Buttplug Out (f8.buttplug_out)
Connect to Intiface/Buttplug with split channels: sendPositionCmd->position, sendFunctionCmd->state.
When to Use
- Use
Buttplug Out when you want to target any hardware supported by the Buttplug.io (Intiface Desktop) ecosystem.
- It acts as the universal bridge for the Feel8 graph, allowing one logic chain to control hundreds of different haptic devices via the Buttplug protocol.
- Ideal for scenarios where the specific hardware is unknown or might be swapped by the end user.
Common Wiring Patterns
- Generic Haptic Out: Feed it cleaned, bounded control values (0.0 to 1.0) from a
Range Map. Avoid sending raw detector or feature outputs directly to the hardware.
- Protocol Separation: Keep your Buttplug-specific configuration separate from your core motion logic to ensure the graph remains portable to other protocols like TCode or LOVENSER.
- Service Monitoring: Use
f8-viz-text to monitor the data being sent to the Buttplug server if a device isn't reacting as expected.
Pitfalls / Gotchas
- Capability Mismatches: Not all devices support the same commands (e.g., some have only "Vibrate," while others have "Linear" or "Rotate"). Verify that your target device supports the command style you are sending from the graph.
- Range Mapping Errors: A bad upstream range (e.g., sending values > 1.0 or < 0.0) will often cause the Buttplug client or server to throw errors or ignore the commands entirely. Always use a
Range Map immediately before this node.
- Server Dependency: This node requires Intiface Desktop or a compatible Buttplug server to be running on the host or network. If the server is not reachable, the node will appear idle or log connection errors.
Operator Reference
- Exec in ports:
sendPositionCmd, sendFunctionCmd
- Exec out ports: none
- Exec inputs:
sendPositionCmd, sendFunctionCmd
- Data inputs:
position
- Data outputs: none
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
enabled |
rw |
true |
true |
boolean / default=True |
Enable connection and output control. |
wsUrl |
rw |
true |
false |
string / default=ws://127.0.0.1:12345 |
Buttplug server websocket URL. Reset to default when exporting publish JSON. |
autoConnect |
rw |
true |
false |
boolean / default=True |
Automatically connect while enabled. |
autoScanOnConnect |
rw |
true |
false |
boolean / default=True |
Start and stop scan once after connect. |
scanDurationMs |
rw |
true |
false |
integer / default=5000 |
Scan duration before stop when scan is triggered. |
reconnectIntervalMs |
rw |
true |
false |
integer / default=2000 |
Reconnect throttle interval. |
selectedDevice |
rw |
true |
true |
string / default= |
Target token: "index|name". |
rescan |
rw |
true |
false |
boolean / default=False |
Set true to trigger one scan cycle; runtime resets it to false. |
vibrateFeatureIndex |
rw |
true |
false |
integer / default=-1 |
Feature index for vibrate (-1 = all). |
rotateFeatureIndex |
rw |
true |
false |
integer / default=-1 |
Feature index for rotate (-1 = all). |
oscillateFeatureIndex |
rw |
true |
false |
integer / default=-1 |
Feature index for oscillate (-1 = all). |
positionFeatureIndex |
rw |
true |
false |
integer / default=-1 |
Feature index for position (-1 = all). |
defaultPositionDurationMs |
rw |
true |
true |
integer / default=500 |
Default duration for position output. |
vibrate |
rw |
true |
false |
number |
Function-channel vibrate intensity (0..1). |
rotate |
rw |
true |
false |
number |
Function-channel rotate speed (-1..1). |
oscillate |
rw |
true |
false |
number |
Function-channel oscillate intensity (0..1). |
stop |
rw |
true |
false |
boolean / default=False |
When true, sendFunctionCmd stops output on selected device. |
stopOnDeactivate |
rw |
true |
false |
boolean / default=True |
Send stop command when service deactivates. |
connected |
ro |
true |
false |
boolean / default=False |
True when websocket is connected. |
scanning |
ro |
true |
false |
boolean / default=False |
True while scanning is active. |
availableDevices |
ro |
true |
false |
array[string] |
Device tokens for selection UI. |
deviceInfos |
ro |
true |
false |
array[object] |
Full discovered device infos. |
selectedDeviceInfo |
ro |
true |
false |
object{displayName, index, inputs, messageTimingGapMs, ...} |
Current selected device info object. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
enabled (Enabled, rw): Enable connection and output control. Schema: boolean / default=True.
wsUrl (WebSocket URL, rw): Buttplug server websocket URL. Reset to default when exporting publish JSON. Schema: string / default=ws://127.0.0.1:12345.
autoConnect (Auto Connect, rw): Automatically connect while enabled. Schema: boolean / default=True.
autoScanOnConnect (Auto Scan On Connect, rw): Start and stop scan once after connect. Schema: boolean / default=True.
scanDurationMs (Scan Duration (ms), rw): Scan duration before stop when scan is triggered. Schema: integer / default=5000.
reconnectIntervalMs (Reconnect Interval (ms), rw): Reconnect throttle interval. Schema: integer / default=2000.
selectedDevice (Selected Device, rw): Target token: "index|name". Schema: string / default=.
rescan (Rescan, rw): Set true to trigger one scan cycle; runtime resets it to false. Schema: boolean / default=False.
| Name |
Required |
On Node |
Schema |
Description |
position |
true |
true |
number |
Position-channel target (0.0001..0.9999) used by sendPositionCmd. |
Data Output Ports
None
- No bundled scenario references this node yet.
Lovense Mock Server (f8.lovense_mock_server)
Event-driven input node that mocks the Lovense Local API and emits received commands.
When to Use
- Use
Lovense Mock Server when you need to test your Lovense local API integrations without having high-end hardware physically connected or powered on.
- It is invaluable for release rehearsals, development of complex haptic adapters, and protocol debugging during rapid iteration.
- Use it to simulate different device types (Lush, Nora, etc.) and verify that your commands are correctly formatted.
Common Wiring Patterns
- Validation Branch: Keep the mock server active on a side branch. Use it to confirm that your
Lovense Out or downstream parser/post-process nodes are receiving the expected Lovense command payloads.
- Protocol Sniffing: Inspect the emitted
event data output and execution triggers using Print or Text Viz nodes to see exactly how the "virtual device" is responding to your graph.
- Automated Testing: Use it in automated scenario tests to verify that a logic chain produces the correct device commands without needing human interaction.
Pitfalls / Gotchas
- Virtual vs Physical: The mock server validates protocol flow and message timing, but it cannot simulate the physical feel, mechanical latency, or battery/Bluetooth nuances of the actual hardware.
- Events Are Data, Not State: Incoming commands can arrive at device-command rate, so the latest command is exposed through the
event data port instead of a high-frequency state field.
- Bind Conflicts: If the mock server fails to start or appears "silent," check if another service (or even the actual Lovense Connect app) is already using the local API ports on your machine.
- Initialization order: The mock server should ideally be started before the nodes that attempt to connect to it.
Operator Reference
- Exec in ports: none
- Exec out ports:
event
- Exec outputs:
event
- Data inputs: none
- Data outputs:
event
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
bindAddress |
rw |
true |
true |
string / default=127.0.0.1 |
Local address to bind (loopback by default). |
allowNonLoopbackBind |
rw |
true |
false |
boolean / default=False |
When true, allow bindAddress values other than loopback. |
port |
rw |
true |
true |
integer / default=30010 |
HTTP port for the mock Lovense server. |
printEnabled |
rw |
true |
false |
boolean / default=False |
If enabled, logs raw incoming requests and outgoing responses (debug). |
eventIncludePayload |
rw |
true |
false |
boolean / default=False |
Include the parsed request payload in the event data output (debug). |
eventIncludeRequest |
rw |
true |
false |
boolean / default=False |
Include request headers/body in the event data output (debug). |
listening |
ro |
true |
true |
boolean / default=False |
True if the HTTP server is currently listening. |
lastError |
ro |
true |
true |
string / default= |
Last server error (e.g. bind failure). |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
bindAddress (Bind Address, rw): Local address to bind (loopback by default). Schema: string / default=127.0.0.1.
allowNonLoopbackBind (Allow Non-loopback Bind, rw): When true, allow bindAddress values other than loopback. Schema: boolean / default=False.
port (Port, rw): HTTP port for the mock Lovense server. Schema: integer / default=30010.
printEnabled (Print Raw IO, rw): If enabled, logs raw incoming requests and outgoing responses (debug). Schema: boolean / default=False.
eventIncludePayload (Event Include Payload, rw): Include the parsed request payload in the event data output (debug). Schema: boolean / default=False.
eventIncludeRequest (Event Include Request, rw): Include request headers/body in the event data output (debug). Schema: boolean / default=False.
listening (Listening, ro): True if the HTTP server is currently listening. Schema: boolean / default=False.
lastError (Last Error, ro): Last server error (e.g. bind failure). Schema: string / default=.
None
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
event |
true |
true |
object{eventId, isoTime, method, path, ...} |
Latest received Lovense command event. |
- No bundled scenario references this node yet.
Sequence Player (f8.sequence_player)
Play a step-sequence over time (epoch-based), outputting the current step value.
When to Use
- Use
Sequence Player to play back pre-recorded or hand-authored motion sequences over time, exposing current signal values, frame indices, and play state.
- It is the primary tool for creating reproducible demos, scripted motion passages, or "preset" movements that can be triggered by scenario logic.
- Use it when you need a high-precision, frame-perfect reproduction of a specific motion pattern.
Common Wiring Patterns
- Preset Triggering: Feed it a sequence payload (e.g., from a JSON file or an upstream generator). Branch the
value output into Range Map or device nodes, and monitor the done port to trigger subsequent state changes.
- Master Progress Sync: Pair it with
Playback Sync if other parts of the graph need to coordinate their behavior based on the sequence's current progress.
- Interactive Playback: Use the
play, pause, and stop commands to control playback dynamically based on user input or detection events.
Pitfalls / Gotchas
- Payload Contract: If the sequence data schema is incorrect (e.g., missing timestamps or wrong data types), the player may fail silently or produce stuttering motion. Validate your data with
f8-viz-text first.
- Ownership Confusion: Avoid having multiple operators fighting for control of the same timeline. Decide whether the
Sequence Player or an external clock (like f8-phase) is the authoritative timebase for a given branch.
- Timing Jitter: Unlike procedural oscillators, sequence playback resolution depends on the engine's tick rate. Ensure your
f8-tick rate is high enough to capture the detail in your recorded sequence.
Operator Reference
- Exec in ports: none
- Exec out ports: none
- Data inputs: none
- Data outputs:
value, index, active, done, elapsedSec
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
sequence |
wo |
true |
true |
object{stepMs, timeSec, tsMs, values} |
Dict payload defining tsMs/stepMs/values/timeSec. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
sequence (Sequence, wo): Dict payload defining tsMs/stepMs/values/timeSec. Schema: object{stepMs, timeSec, tsMs, values}.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
None
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
value |
true |
true |
number |
Current step value. |
index |
true |
true |
integer |
Current 0-based step index. |
active |
true |
true |
boolean |
Whether still playing. |
done |
true |
true |
boolean |
Whether playback ended. |
elapsedSec |
true |
true |
number |
Elapsed seconds since start. |
- No bundled scenario references this node yet.
Silence Detector (f8.silence_detector)
Detect whether a signal has stayed nearly unchanged for long enough to be considered silent.
When to Use
- Use
Silence Detector when you want to detect that a signal has effectively stopped changing and expose that result as sparse graph state.
- It is a good fit for fallback routing, watchdog-style graph logic, and any situation where "no meaningful movement" should trigger another behavior.
- Best when the downstream node should react to a stable state change rather than reading a per-sample analysis signal.
Common Wiring Patterns
- Fallback Switching: Feed the primary signal into
Silence Detector.value, then use graph logic to switch Switch Mixer.currentChannel to a fallback port when isSilent becomes true.
- State-Driven Logic: Pair it with
State Expr, State Trigger, or UI bindings when other graph nodes should respond to silence as a boolean condition.
- Sparse Monitoring:
isSilent is intentionally the only runtime state output; activity timestamps are not published as state because active signals can update at tick rate.
Pitfalls / Gotchas
- Threshold Tuning: If
deltaThreshold is too small, normal noise will keep the node from ever becoming silent; if too large, subtle real motion may be ignored.
- Exec-Driven Sampling: Detection updates on exec, so the effective responsiveness depends on how often the graph drives the node.
- Signal Semantics: It detects lack of change, not low absolute amplitude. A constant non-zero signal still becomes "silent" under this definition.
Operator Reference
- Exec in ports:
exec
- Exec out ports:
exec
- Exec inputs:
exec
- Exec outputs:
exec
- Data inputs:
value
- Data outputs: none
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
silenceMs |
rw |
true |
true |
integer / default=500 |
If the input changes less than deltaThreshold for this long, mark it silent. |
deltaThreshold |
rw |
true |
true |
number / default=0.001 |
Absolute change threshold to treat the input as active. |
isSilent |
ro |
true |
true |
boolean / default=False |
Readonly sparse state output indicating whether the signal is currently silent. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
silenceMs (Silence (ms), rw): If the input changes less than deltaThreshold for this long, mark it silent. Schema: integer / default=500.
deltaThreshold (Delta Threshold, rw): Absolute change threshold to treat the input as active. Schema: number / default=0.001.
isSilent (Is Silent, ro): Readonly sparse state output indicating whether the signal is currently silent. Schema: boolean / default=False.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
| Name |
Required |
On Node |
Schema |
Description |
value |
true |
true |
number |
Signal to analyze |
Data Output Ports
None
- No bundled scenario references this node yet.
Switch Mixer (f8.switch_mixer)
Switch between any number of user-defined input channels with an optional smooth crossfade.
When to Use
- Use
Switch Mixer when your graph needs to select among multiple signal channels or tracks while keeping the transition smooth and controlled.
- It is a good fit for state-driven routing such as primary vs fallback motion, manual override vs automatic control, or switching among several behavior profiles.
- Best for graphs where direct hard switching would feel too abrupt and you want the choice of a short crossfade instead.
Common Wiring Patterns
- Named Channel Routing: Add custom data input ports such as
main, fallback, manual, or track_3, then drive currentChannel from graph state or UI.
- Primary / Fallback Routing: Pair
Silence Detector.isSilent with graph logic that updates currentChannel to a fallback port when the main source becomes inactive.
- Soft Transition Monitoring: Keep a
Wave Viz after the mixer and inspect alpha to verify that transitions are smooth instead of hard cuts.
Pitfalls / Gotchas
- Input Range Mismatch: If your channels are not normalized to similar ranges, the transition can still feel jumpy even with crossfade enabled.
- Missing Control Logic:
Switch Mixer does not decide when to switch; pair it with explicit state logic such as Silence Detector, State Expr, or UI-driven state.
- Hold Semantics: When a selected channel stops receiving valid samples, the mixer holds that channel's last valid value. That is useful for continuity, but it also means stale upstream data can remain audible if your graph never switches away.
- Too Much Fade: Very large
fadeMs values can make the graph feel sluggish or indecisive when the control condition changes quickly.
Operator Reference
- Exec in ports:
exec
- Exec out ports:
exec
- Exec inputs:
exec
- Exec outputs:
exec
- Data inputs:
ch1, ch2
- Data outputs:
out, alpha
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
currentChannel |
rw |
true |
true |
string / default=ch1 |
Name of the selected input channel/track to play. |
resolvedChannel |
ro |
true |
true |
string / default= |
Readonly currently resolved input channel after validation/fallback. |
fadeMs |
rw |
true |
true |
integer / default=200 |
Transition duration in milliseconds. Set to 0 for an instant switch. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
currentChannel (Current Channel, rw): Name of the selected input channel/track to play. Schema: string / default=ch1.
resolvedChannel (Resolved Channel, ro): Readonly currently resolved input channel after validation/fallback. Schema: string / default=.
fadeMs (Fade (ms), rw): Transition duration in milliseconds. Set to 0 for an instant switch. Schema: integer / default=200.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
| Name |
Required |
On Node |
Schema |
Description |
ch1 |
false |
true |
number |
Input channel 1 |
ch2 |
false |
true |
number |
Input channel 2 |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
out |
true |
true |
number |
Mixed output |
alpha |
true |
true |
number |
Transition progress (0..1) |
- No bundled scenario references this node yet.
Playback Sync (f8.playback_sync)
Extrapolates IMPlayer playback position between sparse playback state updates.
When to Use
- Use
Playback Sync when your graph needs to be aware of the timeline, progress, or state of a media stream or sequence playback (active, paused, seeking).
- It is the primary tool for tying complex motion logic to a shared external playback clock (e.g., from
f8-implayer or f8-sequence-player).
- Use it to synchronize state machine transitions with specific timestamps in a media file.
Common Wiring Patterns
- Master Clock Lock: Pair it with an
IM Player or Sequence Player source. Feed the resulting progress into downstream operators to ensure they stay perfectly in sync with the media content.
- UI Progress Monitoring: Route the progress and duration outputs to
Text Viz or a custom dashboard to give the user a visual indication of the current scenario timeline.
- Conditional Scripting: Use the
active or looping status to enable/disable specific parts of your graph based on whether media is currently playing.
Pitfalls / Gotchas
- Timebase Competition: Drift and unexpected "jump" resets are common when multiple nodes in a graph try to be the authoritative timebase. Choose ONE source as the master clock and use
Playback Sync to distribute it.
- Granularity Differences: If your sync source has low time resolution, downstream motion may appear "steppy." Consider using a
Smooth Filter to interpolate between progress updates if perfectly smooth motion is required.
- Disconnection Handling: Always consider what should happen if the playback source is stopped or disconnected. Use default values or fallback logic to prevent actuators from getting "stuck" at a specific position.
Operator Reference
- Exec in ports: none
- Exec out ports: none
- Data inputs:
playback
- Data outputs:
position, rawPosition, duration, playing, videoId, ageMs, stale
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
maxExtrapolateMs |
rw |
true |
false |
integer / default=1000 |
Limit extrapolation horizon to avoid drift when playback state is stale (0 = unlimited). |
playbackRate |
rw |
true |
false |
number / default=1.0 |
Rate multiplier used for extrapolation when playing. |
clampToDuration |
rw |
true |
false |
boolean / default=True |
Clamp estimated position to latest duration when available. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
maxExtrapolateMs (Max Extrapolate (ms), rw): Limit extrapolation horizon to avoid drift when playback state is stale (0 = unlimited). Schema: integer / default=1000.
playbackRate (Playback Rate, rw): Rate multiplier used for extrapolation when playing. Schema: number / default=1.0.
clampToDuration (Clamp To Duration, rw): Clamp estimated position to latest duration when available. Schema: boolean / default=True.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
| Name |
Required |
On Node |
Schema |
Description |
playback |
false |
true |
object{duration, playing, position, videoId} |
Playback payload from f8.implayer/playback (position/duration/playing/videoId). |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
position |
true |
true |
number |
Estimated playback position (seconds). |
rawPosition |
true |
false |
number |
Latest raw position from playback payload (seconds). |
duration |
true |
false |
number |
Latest duration (seconds). |
playing |
true |
false |
boolean |
Latest playing flag. |
videoId |
true |
false |
string |
Latest video id. |
ageMs |
true |
false |
integer |
Age of latest playback sample in milliseconds. |
stale |
true |
false |
boolean |
True if sample age exceeds max extrapolation window. |
- No bundled scenario references this node yet.
Handy Out (f8.handy_out)
Drive The Handy via HDSP using normalized 0..1 input values.
When to Use
- Use
Handy Out when your graph needs to send TCode commands directly to "The Handy" device over a network connection.
- It is a specialized device sink that handles the specific transport requirements for The Handy while maintaining the Feel8 graph's temporal consistency.
- Use it to synchronize your computer vision or audio-driven logic with a physical Handy device in real-time.
Common Wiring Patterns
- Standard Handy Setup: Feed it finalized
TCode strings from a f8-tcode operator. Ensure your scaling and safety limits are applied upstream.
- Monitoring Bridge: Keep an
f8-viz-text or TCodeViz attached to the input port while validating your connection to the device's API.
- Connection Management: Use the node properties to manage the Handy's connection key and transport mode (e.g., WebSocket vs REST).
Pitfalls / Gotchas
- Transport Role: Treat this node strictly as a communication layer. Do not attempt to fix signal timing or motion logic here; those issues should be addressed in the
f8-tcode or Range Map nodes.
- Latency Over network: Commands sent to The Handy are subject to network jitter. If the motion feels "stuck" or delayed, check your local Wi-Fi stability and the
intervalMs setting to ensure you aren't overwhelming the device's buffer.
- Command Telemetry Is Data, Not State: HTTP status/result and sent-position diagnostics are emitted on data ports. They are not stored as state because successful commands can occur at motion tick rate.
- Key Sensitivity: The connection key is sensitive information. Avoid sharing session files that contain your unique device key if you are collaborating with others.
Operator Reference
- Exec in ports:
exec
- Exec out ports: none
- Exec inputs:
exec
- Data inputs:
value, durationMs, immediateResponse, stopOnTarget
- Data outputs:
sentPosition, httpStatus, result, error
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
enabled |
rw |
true |
true |
boolean / default=True |
Enable/disable Handy output. |
connectionKey |
rw |
true |
true |
string / default= |
The Handy X-Connection-Key value. |
baseUrl |
rw |
true |
true |
string / default=https://www.handyfeeling.com/api/handy/v2 |
Handy API base URL. Reset to default when exporting publish JSON. |
ensureHdspMode |
rw |
true |
true |
boolean / default=True |
Automatically set mode=HDSP before sending position commands. |
invert |
rw |
true |
true |
boolean / default=False |
Invert 0..1 input mapping before percent conversion. |
minPercent |
rw |
true |
true |
number / default=0.0 |
Mapped output minimum in percent. |
maxPercent |
rw |
true |
true |
number / default=100.0 |
Mapped output maximum in percent. |
defaultDurationMs |
rw |
true |
true |
integer / default=100 |
Default /hdsp/xpt duration when durationMs input is not provided. |
requestTimeoutMs |
rw |
true |
false |
integer / default=5000 |
HTTP request timeout for Handy API calls. |
minSendIntervalMs |
rw |
true |
true |
integer / default=0 |
Minimum interval between sent commands (0 means follow tick rate). |
immediateResponse |
rw |
true |
false |
boolean / default=False |
Default immediateResponse value for /hdsp/xpt. |
stopOnTarget |
rw |
true |
false |
boolean / default=False |
Default stopOnTarget value for /hdsp/xpt. |
lastError |
ro |
false |
true |
string / default= |
Last runtime error message. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
enabled (Enabled, rw): Enable/disable Handy output. Schema: boolean / default=True.
connectionKey (Connection Key, rw): The Handy X-Connection-Key value. Schema: string / default=.
baseUrl (Base URL, rw): Handy API base URL. Reset to default when exporting publish JSON. Schema: string / default=https://www.handyfeeling.com/api/handy/v2.
ensureHdspMode (Ensure HDSP Mode, rw): Automatically set mode=HDSP before sending position commands. Schema: boolean / default=True.
invert (Invert, rw): Invert 0..1 input mapping before percent conversion. Schema: boolean / default=False.
minPercent (Min Percent, rw): Mapped output minimum in percent. Schema: number / default=0.0.
maxPercent (Max Percent, rw): Mapped output maximum in percent. Schema: number / default=100.0.
defaultDurationMs (Default Duration (ms), rw): Default /hdsp/xpt duration when durationMs input is not provided. Schema: integer / default=100.
| Name |
Required |
On Node |
Schema |
Description |
value |
true |
true |
number |
Normalized position input (0..1). |
durationMs |
false |
true |
number / default=100 |
Optional duration override for /hdsp/xpt. |
immediateResponse |
false |
true |
boolean / default=False |
Optional immediate response override for /hdsp/xpt. |
stopOnTarget |
false |
true |
boolean / default=False |
Optional stopOnTarget override for /hdsp/xpt. |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
sentPosition |
true |
true |
number / default=0.0 |
Last sent position percent (0..100). |
httpStatus |
true |
true |
integer / default=0 |
Last HTTP status code. |
result |
true |
true |
number / default=0.0 |
Last RPC result code. |
error |
true |
true |
string / default= |
Last runtime error. |
- No bundled scenario references this node yet.
State Trigger (f8.state_trigger)
Triggers exec on changed when state value changes; ideal for wiring button-like state changes into exec graphs.
When to Use
- Use
State Trigger to fire an execution (exec) signal only when a specifically watched state value or data input changes.
- It is the most efficient way to handle "event-like" reactions (e.g., a button press, a mode change) without polling or calculating every single tick.
- Use it to trigger initialization sequences or to update "static" UI elements only when new data is actually available.
Common Wiring Patterns
- Event Gating: Feed a stateful value from a
Control Panel or a service state edge into it. Use the changed exec output to trigger side effects like re-configuring a service or starting a sequence.
- Data Cleanup: Use it to clear a diagnostic visualizer or log a message only when a "Done" signal or an error status actually occurs.
- Property Binding: Link the output to a node property that should only be updated when its input source changes, reducing redundant processing in downstream nodes.
Pitfalls / Gotchas
- High-Frequency Traps: If the watched value changes every frame (e.g., raw bone coordinates or a noise signal), this node becomes effectively another high-frequency
Tick source, negating its efficiency benefits.
- Missed Changes: Ensure that your graph logic relies on changes rather than states. If you need a continuous check, use a
Tick node instead.
- Hysteresis Requirements: For noisy signals that flip-flop between values, consider adding a
Smooth Filter or a small amount of dead-zone logic upstream to prevent the trigger from firing too often.
Operator Reference
- Exec in ports: none
- Exec out ports:
changed
- Exec outputs:
changed
- Data inputs: none
- Data outputs: none
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
value |
rw |
false |
true |
any |
Watched state value. Exec emits when this changes. |
enabled |
rw |
true |
true |
boolean / default=True |
Enable/disable trigger emission on value changes. |
fireOnStart |
rw |
true |
true |
boolean / default=False |
If enabled and value has an initial value, emit one exec when the node entrypoint starts. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
value (Value, rw): Watched state value. Exec emits when this changes. Schema: any.
enabled (Enabled, rw): Enable/disable trigger emission on value changes. Schema: boolean / default=True.
fireOnStart (Fire On Start, rw): If enabled and value has an initial value, emit one exec when the node entrypoint starts. Schema: boolean / default=False.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
None
Data Output Ports
None
- No bundled scenario references this node yet.
State Expr (f8.state_expr)
Evaluate a small Python expression using state fields as symbols.
Core
- Editable RW/WO state fields become expression symbols directly.
- The expression result is published to read-only state out.
- State names that are not valid Python identifiers remain available through states['field-name'].
- No data ports are involved.
Available
- Builtins: abs, min, max, round, float, int, len, sum, sorted, range, any, all, sigmoid
Examples
- sigmoid(x)
- a + b
- config.center.x
- states['left-value'] * gain
- math.sin(phase)
When to Use
- Use
State Expr when you need to derive a computed value or "virtual property" from other editable state fields on the same node, rather than from incoming data ports.
- It is excellent for creating formulas, derived parameters, and lightweight control math that should stay visible and tunable directly on the node's property panel.
- Use it to enforce relationships between parameters (e.g.,
max_speed = base_speed * multiplier).
Common Wiring Patterns
- Derived Parameters: Add multiple numeric state fields (properties) as your input variables. The operator automatically exposes these as symbols in your expression. Publish the result (
out) to other nodes or use it for Studio inspection.
- Live Tuning: Keep the expression focused on a small set of clearly named fields so the system remains easy to calibrate during a live session.
- Inspector Feedback: Use it to create "Read-only" calculated fields that summarize the current state of a complex operator group for easier monitoring.
Pitfalls / Gotchas
- Type Restrictions: Only writable numeric state fields (float, int) are automatically extracted as symbols. Non-numeric fields or read-only properties will not be available in the expression.
- Error Visibility: If an expression fails (e.g., division by zero), the operator will publish the error to its
lastError field and clear the output. If your downstream graph seems "stuck," check the lastError field first.
- Circular Dependencies: Be careful not to create logical loops where an expression depends on a value that is eventually affected by its own output, as this can lead to unstable behavior.
Operator Reference
- Exec in ports: none
- Exec out ports: none
- Data inputs: none
- Data outputs: none
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
allowNumpy |
rw |
false |
false |
boolean / default=False |
Enable np.* and numpy.* inside the expression. |
code |
rw |
false |
true |
string / default=0 |
Single Python expression. Editable RW/WO state fields are available directly by name; non-identifier names remain available through states[...]. |
out |
ro |
false |
true |
any |
Expression result published by the node. |
lastError |
ro |
false |
false |
string / default= |
Last compile or evaluation error. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
allowNumpy (Allow Numpy, rw): Enable np.* and numpy.* inside the expression. Schema: boolean / default=False.
code (Expr, rw): Single Python expression. Editable RW/WO state fields are available directly by name; non-identifier names remain available through states[...]. Schema: string / default=0.
out (Out, ro): Expression result published by the node. Schema: any.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
None
Data Output Ports
None
- No bundled scenario references this node yet.
Bone Filter (f8.bone_filter)
Smooths a single bone pose and outputs filtered + local relative pose.
When to Use
- Use
Bone Filter when you need to stabilize and normalize a skeleton bone pose (usually from MediaPipe or a VMC stream) into a local, jitter-free control signal.
- It is essential for skeleton-driven control graphs where sensor noise or tracking jitters would otherwise cause physical actuators to "chatter."
- Ideal for calculating relative angles or distances between bones (e.g., wrist position relative to shoulder) with built-in temporal smoothing.
Common Wiring Patterns
- Stable Control Loop: Feed it from a
Bone Selector operator. Use the filtered (smoothed world space) or relative (position relative to a parent bone) outputs for Range Map or Euler conversion.
- Visual Calibration: Tune the filter properties (like
alpha or cutoff) while watching a live skeleton in f8-viz-three-d to find the best balance between jitter reduction and following lag.
- Gesture Normalization: Use the
relative output to drive gesture-recognition logic that should ignore the subject's overall position in the room.
Pitfalls / Gotchas
- Correct Bone Verification: Do not attempt to tune the filter coefficients before confirming that the
Bone Selector is actually providing the correct bone data stream.
- Jump Reset Latency: If your "Jump Reset" (threshold for ignoring large discontinuities) is too aggressive, it may cause the skeleton to "stick" or lag behind when the subject moves rapidly.
- Coordinate Systems: Ensure your world-space assumptions match the upstream source (e.g., MediaPipe's flipped Y-axis) before relying on the filtered relative positions.
Operator Reference
- Exec in ports: none
- Exec out ports: none
- Data inputs:
bone
- Data outputs:
filtered, relative
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
filter_type |
rw |
true |
true |
string / enum[NONE, EMA, DEMA, ONEEURO] / default=EMA |
Filter type. |
ema_alpha |
rw |
true |
true |
number / default=0.4 |
EMA smoothing factor (0..1). |
dema_alpha |
rw |
true |
true |
number / default=0.4 |
DEMA smoothing factor (0..1). |
one_euro_min_cutoff |
rw |
true |
true |
number / default=1.5 |
Minimum cutoff frequency. |
one_euro_beta |
rw |
true |
true |
number / default=0.0 |
Speed coefficient for dynamic cutoff. |
one_euro_derivative_cutoff |
rw |
true |
false |
number / default=1.0 |
Cutoff frequency for derivative filter. |
one_euro_default_freq |
rw |
true |
false |
number / default=90.0 |
Default sampling frequency (Hz). |
jumpEnabled |
rw |
true |
false |
boolean / default=True |
Enable jump detection and hard reset. |
jumpPosThreshold |
rw |
true |
false |
number / default=0.25 |
Position distance threshold. |
jumpRotDegThreshold |
rw |
true |
false |
number / default=35.0 |
Rotation distance threshold in degrees. |
jumpConsecutiveFrames |
rw |
true |
false |
number / default=3 |
Consecutive far frames required before reset. |
jumpCooldownFrames |
rw |
true |
false |
number / default=8 |
Cooldown frames after reset. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
filter_type (Filter, rw): Filter type. Schema: string / enum[NONE, EMA, DEMA, ONEEURO] / default=EMA.
ema_alpha (EMA Alpha, rw): EMA smoothing factor (0..1). Schema: number / default=0.4.
dema_alpha (DEMA Alpha, rw): DEMA smoothing factor (0..1). Schema: number / default=0.4.
one_euro_min_cutoff (One Euro Min Cutoff, rw): Minimum cutoff frequency. Schema: number / default=1.5.
one_euro_beta (One Euro Beta, rw): Speed coefficient for dynamic cutoff. Schema: number / default=0.0.
one_euro_derivative_cutoff (One Euro Deriv Cutoff, rw): Cutoff frequency for derivative filter. Schema: number / default=1.0.
one_euro_default_freq (One Euro Default Freq, rw): Default sampling frequency (Hz). Schema: number / default=90.0.
jumpEnabled (Jump Enabled, rw): Enable jump detection and hard reset. Schema: boolean / default=True.
| Name |
Required |
On Node |
Schema |
Description |
bone |
true |
true |
object{pos, rot} |
Input bone pose with pos[3] and rot[4] quaternion. |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
filtered |
true |
true |
object{pos, rot} |
Filtered bone pose. |
relative |
true |
true |
object{pos, rot} |
Relative pose in filtered local space. |
- No bundled scenario references this node yet.
Quat To Euler (f8.quat_to_euler)
Converts quaternion [w,x,y,z] to Euler angles with configurable order.
When to Use
- Use
Quat To Euler when you need to convert 3D rotation data (Quaternions) into human-readable Euler angles (Pitch, Yaw, Roll) or axis-specific scalar values.
- It is most useful at the boundary between raw skeleton math and physical actuator control logic, where you need to map a specific joint's rotation to a haptic axis.
- Use it to extract "Tilt" or "Twist" intensities from an limb or a tracked object.
Common Wiring Patterns
- Joint-to-Actuator Mapping: Feed it from a
Bone Selector or Bone Filter. Map the resulting Euler components (X, Y, or Z) through a Range Map to drive device outputs.
- Orientation Debugging: Route the Euler angles into
f8-viz-wave to visually analyze the range of motion before setting final control thresholds.
- Reference Frame Alignment: Specify the
order (e.g., XYZ, YZX) and the unit (Degrees vs. Radians) clearly in your configuration to match your downstream control hardware requirements.
Pitfalls / Gotchas
- Gimbal Lock: Euler angles can become unstable at certain orientations (Gimbal Lock). If your control values "flip" suddenly when a joint reaches a specific angle, you may need a different rotation order or a more robust quaternion-based logic upstream.
- Rotation Order Errors: Choosing the wrong rotation order can produce motion that looks "correct" on one axis but behaves unpredictably on others. Always verify against a visual skeleton reference.
- Late Conversion: Try to keep your math in Quaternion form as long as possible. Only convert to Euler at the very end of your chain to avoid mathematical artifacts during smoothing or interpolation.
Operator Reference
- Exec in ports: none
- Exec out ports: none
- Data inputs:
quat
- Data outputs:
euler
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
order |
rw |
true |
true |
string / enum[XYZ, XZY, YXZ, YZX, ZXY, ...] / default=ZYX |
Euler rotation order. |
degrees |
rw |
true |
true |
boolean / default=True |
Output in degrees when true, radians when false. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
order (Order, rw): Euler rotation order. Schema: string / enum[XYZ, XZY, YXZ, YZX, ZXY, ...] / default=ZYX.
degrees (Degrees, rw): Output in degrees when true, radians when false. Schema: boolean / default=True.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
| Name |
Required |
On Node |
Schema |
Description |
quat |
false |
true |
array[number] |
Input quaternion [w,x,y,z]. |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
euler |
true |
true |
array[number] |
Euler angles [x,y,z] in selected order. |
- No bundled scenario references this node yet.
VMC Decoder (f8.vmc_decoder)
Decodes udp_in packet payloads carrying VMC OSC messages into skeleton streams.
When to Use
- Use
VMC Decoder after UDP In when the incoming UDP packets carry VMC OSC messages.
- Use it as the decoding stage in a
UDP In -> VMC Decoder chain for live VMC streams.
- This split keeps OSC/VMC decoding independent from socket lifecycle, which makes graphs more composable and easier to debug.
Common Wiring Patterns
- Avatar Pipeline: Connect
UDP In.packet to VMC Decoder.packet, then use selectedSkeleton for Bone Selector, Bone Filter, or avatar-driven control logic.
- Live VMC Debugging: Keep a
Print or Text Viz branch on UDP In.packet while the main branch feeds VMC Decoder.
- Multi-Model Selection: Use
availableKeys and selectedKey to lock onto the intended avatar when multiple identities are present.
Pitfalls / Gotchas
- Binary Payload Required: VMC is an OSC-based binary protocol. Feed this node with
UDP In.packet or direct raw bytes so decoding reads the original payload, not text/json views.
- Decoder Placement: Put VMC-specific logic after
VMC Decoder, not before; upstream nodes should stay transport-oriented.
- Packet Contract: Feed this node with
UDP In.packet so OSC/VMC decoding stays isolated from socket management.
Operator Reference
- Exec in ports:
packet
- Exec out ports:
packet
- Exec inputs:
packet
- Exec outputs:
packet
- Data inputs:
packet
- Data outputs:
skeletons, selectedSkeleton
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
cleanupAfterMs |
rw |
true |
false |
integer / default=10000 |
Remove models that haven't updated for this many ms (<=0 disables cleanup). |
selectedKey |
rw |
true |
true |
string / default= |
If set and matches an available key, outputs selectedSkeleton; otherwise None. |
availableKeys |
ro |
true |
true |
array[string] |
Read-only list of current keys (updated only on changes). |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
cleanupAfterMs (Cleanup After (ms), rw): Remove models that haven't updated for this many ms (<=0 disables cleanup). Schema: integer / default=10000.
selectedKey (Selected Key, rw): If set and matches an available key, outputs selectedSkeleton; otherwise None. Schema: string / default=.
availableKeys (Available Keys, ro): Read-only list of current keys (updated only on changes). Schema: array[string].
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
| Name |
Required |
On Node |
Schema |
Description |
packet |
true |
true |
any |
Packet payload from udp_in.packet. |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
skeletons |
true |
true |
array[object] |
List of latest payloads (ordered by key). |
selectedSkeleton |
true |
true |
object{boneCount, bones, modelName, schema, ...} |
Latest payload matching selectedKey (or None). |
- No bundled scenario references this node yet.
Bone Selector (f8.bone_selector)
Selects one bone from a skeleton by target and outputs {name,pos,rot}.
When to Use
- Use
Bone Selector to "extract" a single named bone (e.g., RightWrist, Pelvis) from a full skeleton payload provided by an upstream ingest node.
- It acts as the primary bridge between a dense skeleton stream (containing dozens of joints) and your specific, bone-targeted control logic.
- Use it to isolate a specific joint's movement for specialized analysis or device mapping.
Common Wiring Patterns
- Joint Isolation: Feed it from
Skeleton Decoder, VMC Decoder, or a MediaPipe source. Pass the resulting single-bone payload into a Bone Filter or Quat To Euler operator.
- Multi-Bone Processing: Use multiple
Bone Selector nodes in parallel to extract different joints (e.g., both hands) for a coordinated interaction scenario.
- Dynamic Selection: Use the
availableBones output list in conjunction with a Control Panel to interactively switch which joint your graph is following during a tuning session.
Pitfalls / Gotchas
- Naming Discrepancies: A "missing" bone is often just a naming mismatch (e.g.,
Hips vs. Pelvis) between the source stream and your selector configuration. Check the availableBones port to see valid names for your current source.
- Stream Continuity: This node depends on a valid, continuous skeleton payload. If the upstream detector loses the person, this node will stop producing updates.
- Abstraction Limits: This node only selects data; it does not perform any math or transformation. Use
Bone Filter or Quat To Euler for subsequent processing.
Operator Reference
- Exec in ports: none
- Exec out ports: none
- Data inputs:
skeleton
- Data outputs:
bone
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
target |
rw |
true |
true |
string / default= |
Bone name to select. |
availableBones |
ro |
true |
false |
array[string] |
Read-only list of available bone names from current skeleton input. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
target (Target Bone, rw): Bone name to select. Schema: string / default=.
availableBones (Available Bones, ro): Read-only list of available bone names from current skeleton input. Schema: array[string].
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
| Name |
Required |
On Node |
Schema |
Description |
skeleton |
true |
true |
object{bones} |
Single skeleton payload (e.g. skeleton_decoder.selectedSkeleton or vmc_decoder.selectedSkeleton). |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
bone |
true |
true |
object{name, pos, rot} |
Selected bone payload {name,pos,rot} or None. |
- No bundled scenario references this node yet.
Wave Expr (f8.wave_expr)
Template-based waveform expression node.
Core
- t is cycle-domain input, not radians.
- Runtime output evaluates with t % maxT.
- Any numeric RW/WO state field can be referenced by name.
- express shows the final formula after numeric state substitution.
- Preview shows sampled [t, value] pairs over [0, maxT).
Oscillators
- Phase trig: sin, cos, tan, asin, acos, atan, atan2
- Shape helpers: saw, tri, pulse
- Tempest: tempest(t, p, c) where t is 0..1 circle phase, p is phase, c is eccentricity
Utility
- Blend and range: clamp, lerp, smoothstep, saturate
- Phase helpers: frac, wrap
- Selection: cond(condition, a, b)
- Sequence: sequence([a, b, c]) uses int(t) % len(sequence)
- Numeric helpers: abs, min, max, round, floor, ceil, sqrt, exp, log, log10
Examples
- 0.5 + 0.5 * cos(t)
- sequence([10, 20, 30, 20])
- tempest(t, 0, c)
- cond(t > 4, 1, 0)
When to Use
- Use
Wave Expr to generate a procedural, looping waveform defined by a mathematical expression (e.g., sin(t * 2 * pi)) rather than by manually drawing points or keyframes.
- It is ideal for creating reusable modulation shapes (LFOs) that depend on time
t and other parameters exposed as node properties.
- Use it when you need perfectly consistent, algorithmic motion that remains stable over long periods.
Common Wiring Patterns
- Clock-Driven LFO: Drive the
t input from a f8-phase or f8-tick clock source. Feed the resulting value output into an actuator-facing Range Map or Envelope operator.
- Parametric Modulation: Expose a small set of properties (e.g.,
freq, amp) as variables. Use them in your expression (e.g., amp * sin(t * freq)) to allow interactive tuning of the wave shape without editing the code.
- Wave Combining: Use the output of one
Wave Expr to modulate the frequency or amplitude of another to create complex, evolving patterns.
Pitfalls / Gotchas
- Reserved Symbols: The expression language has reserved names for mathematical functions. Avoid using variable names that collide with these functions (e.g., don't name a property
sin).
- Loop Period Alignment: The
maxT property defines the cycle period. If your upstream time source and the maxT setting disagree, your waveform may appear to "jump" or stutter at the end of each cycle.
- Time Continuity: Sudden jumps in the
t input will cause immediate jumps in the output. If you are syncing to an external timeline, consider using a Smooth Filter downstream to handle discontinuities.
Operator Reference
- Exec in ports: none
- Exec out ports: none
- Data inputs:
t
- Data outputs:
value
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
template |
wo |
true |
true |
string / default=0.5 + 0.5 * cos(t) |
Waveform expression template. t is cycle-domain, numeric state fields can be referenced by name, and helpers include cond, sequence([...]), tempest(t, p, c), phase trig, and shaping functions. |
maxT |
rw |
true |
true |
number / default=10.0 |
Cycle horizon for wrapping and preview sampling. Runtime output uses t % maxT; preview samples [0, maxT). |
minValue |
rw |
true |
false |
number / default=0.0 |
Preview window lower bound (Y-axis). Auto zoom when minValue >= maxValue. |
maxValue |
rw |
true |
false |
number / default=0.0 |
Preview window upper bound (Y-axis). Auto zoom when minValue >= maxValue. |
express |
ro |
true |
false |
string / default= |
Rendered expression after numeric state substitution. t remains symbolic so you can inspect the final formula. |
preview |
ro |
true |
true |
array[array] / default=[] |
Preview waveform samples as [t, value] pairs over [0, maxT). Changes in preview coordinates trigger redraw. |
lastError |
ro |
true |
false |
string / default= |
Last template compile, preview evaluation, or runtime evaluation error. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
template (Template, wo): Waveform expression template. t is cycle-domain, numeric state fields can be referenced by name, and helpers include cond, sequence([...]), tempest(t, p, c), phase trig, and shaping functions. Schema: string / default=0.5 + 0.5 * cos(t).
maxT (Max T, rw): Cycle horizon for wrapping and preview sampling. Runtime output uses t % maxT; preview samples [0, maxT). Schema: number / default=10.0.
minValue (Min Value, rw): Preview window lower bound (Y-axis). Auto zoom when minValue >= maxValue. Schema: number / default=0.0.
maxValue (Max Value, rw): Preview window upper bound (Y-axis). Auto zoom when minValue >= maxValue. Schema: number / default=0.0.
express (Express, ro): Rendered expression after numeric state substitution. t remains symbolic so you can inspect the final formula. Schema: string / default=.
preview (Preview, ro): Preview waveform samples as [t, value] pairs over [0, maxT). Changes in preview coordinates trigger redraw. Schema: array[array] / default=[].
lastError (Last Error, ro): Last template compile, preview evaluation, or runtime evaluation error. Schema: string / default=.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
| Name |
Required |
On Node |
Schema |
Description |
t |
true |
true |
number |
Scalar cycle-domain input. 1.0 means one period; output evaluation uses t % maxT. |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
value |
true |
true |
number |
Expression output value for the current wrapped t sample. |
- No bundled scenario references this node yet.
Wave Pattern (f8.wave_pattern)
Interactive periodic waveform node.
Core
- t is cycle-domain input and runtime output evaluates at t % maxT.
- points stores editable control points as [t, value] pairs.
- interp selects the interpolation method for preview and runtime evaluation.
- Preview samples the periodic waveform over [0, maxT).
When to Use
- Use
Wave Pattern when you want to design a looping or one-shot waveform by manually placing control points on a timeline rather than writing code or using simple oscillators.
- It is the best choice for hand-tuned motion shapes where a designer needs precise control over every peak, valley, and transition.
- Ideal for complex, non-periodic gestures or repeating patterns that cannot be easily described by a sine wave.
Common Wiring Patterns
- Designer LFO: Feed the
t input from a f8-phase or timeline source. Edit the points and interp (interpolation) properties to shape the motion, then send the result into a Range Map or output operator.
- Gesture Library: Keep a collection of
Wave Pattern nodes as a "library" of presets. Switch between them using logic in f8-pyengine.
- Interpolation Tuning: Experiment with different interpolation modes (
pchip, akima, linear) while watching the output on f8-viz-wave to find the most natural feel.
Pitfalls / Gotchas
- Interpolation Overshoot: Non-linear interpolation modes like
spline or pchip can introduce extra "hills" or "valleys" between your points if the points are spaced too closely or unevenly. Monitor the result visually to avoid unwanted movement.
- Cycle Wraparound: The
maxT property defines the loop boundary. If your last point doesn't align with your first point at maxT, you will see a sharp "jump" when the wave repeats.
- Point Density: Keep the point list as sparse as possible. Adding too many unnecessary points makes the pattern harder to tune and can lead to jittery motion.
Operator Reference
- Exec in ports: none
- Exec out ports: none
- Data inputs:
t
- Data outputs:
value
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
points |
rw |
true |
true |
array[array] / default=[[0.0, 0.0], [10.0, 0.0]] |
Editable control points as [t, value] pairs. |
maxT |
rw |
true |
true |
number / default=10.0 |
Cycle horizon for wrapping and preview sampling. |
minValue |
rw |
true |
true |
number / default=0.0 |
Editor and preview lower Y bound. |
maxValue |
rw |
true |
true |
number / default=1.0 |
Editor and preview upper Y bound. |
interp |
rw |
true |
true |
string / enum[linear, pchip, akima, cubic_spline] / default=pchip |
Interpolation method used to generate the periodic waveform. |
preview |
ro |
true |
false |
array[array] |
Preview waveform samples as [t, value] pairs over [0, maxT). |
lastError |
ro |
true |
false |
string / default= |
Last interpolation build or preview evaluation error. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
points (Points, rw): Editable control points as [t, value] pairs. Schema: array[array] / default=[[0.0, 0.0], [10.0, 0.0]].
maxT (Max T, rw): Cycle horizon for wrapping and preview sampling. Schema: number / default=10.0.
minValue (Min Value, rw): Editor and preview lower Y bound. Schema: number / default=0.0.
maxValue (Max Value, rw): Editor and preview upper Y bound. Schema: number / default=1.0.
interp (Interp, rw): Interpolation method used to generate the periodic waveform. Schema: string / enum[linear, pchip, akima, cubic_spline] / default=pchip.
preview (Preview, ro): Preview waveform samples as [t, value] pairs over [0, maxT). Schema: array[array].
lastError (Last Error, ro): Last interpolation build or preview evaluation error. Schema: string / default=.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
| Name |
Required |
On Node |
Schema |
Description |
t |
true |
true |
number |
Scalar cycle-domain input. Runtime evaluation uses t % maxT. |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
value |
true |
true |
number |
Interpolated waveform output for the current wrapped t sample. |
- No bundled scenario references this node yet.
Wave Funscript (f8.wave_funscript)
Load a .funscript JSON file and expose one axis as a looping linear waveform.
t is in seconds and evaluation uses t % maxT.
When to Use
- Use
Wave Funscript when the motion in your graph should be driven by an existing authored .funscript file (commonly used in haptic media) rather than a synthetic oscillator or a hand-drawn pattern.
- It is the primary tool for achieving repeatable, frame-accurate playback of synchronized haptic scripts within the Feel8 system.
- Best for scenarios where you want to "remix" or "re-map" an existing script to different hardware or use it as a part of a larger hybrid automation graph.
Common Wiring Patterns
- Scripted Playback: Point the node to a local
.funscript file. Select the desired axis (usually "L0"), drive the t input from the current playback time (e.g., from Playback Sync), and route the resulting value to your hardware out nodes.
- Dynamic Rescaling: Pass the
value output through a Range Map to adjust script intensity in real-time while it is playing.
- Interpolation Sweep: Start with the default
linear interpolation for a faithful reproduction of the original author's intent. Only use smoother modes like spline if the script points are too sparse and cause mechanical jitter on your specific device.
Pitfalls / Gotchas
- Duration Mismatches: If your timing source (
t) exceeds the duration of the funscript, the wave will loop or stop depending on the wrap settings. ensure your master clock matches the script's expectations.
- Axis Confusion: Many funscripts contain multiple axes of data. Verify you have selected the correct axis (e.g.,
L0 for stroke, V0 for vibration) in the node properties.
- Interpolation Overshoot: Smoother interpolation modes can sometimes introduce "overshoot" or "wobble" between far-apart points that was not in the original script. Always monitor the wave shape in
f8-viz-wave before final use.
Operator Reference
- Exec in ports: none
- Exec out ports: none
- Data inputs:
t
- Data outputs:
value
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
funscriptPath |
rw |
true |
true |
string / default= |
Path to a .funscript JSON file. Cleared when exporting publish JSON. |
allAxes |
ro |
true |
false |
array[string] |
Available funscript channels including the TopLevel pseudo-axis. |
selectedAxis |
rw |
true |
true |
string / default=TopLevel |
Axis id to load from the funscript file. |
points |
ro |
true |
false |
array[array] / default=[] |
Normalized [timeSec, pos01] points loaded from the selected axis. |
maxT |
rw |
true |
true |
number / default=10.0 |
Loop period in seconds. Initialized from the funscript duration and user-overridable. |
interp |
rw |
true |
true |
string / enum[linear, pchip, akima, cubic_spline] / default=linear |
Interpolation method used for runtime output. Heatmap remains linear. |
heatmap |
ro |
true |
true |
array[number] / default=[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] |
Per-time-bin activity heatmap derived from the selected axis. |
lastError |
ro |
true |
false |
string / default= |
Last load or parse error. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
funscriptPath (Funscript Path, rw): Path to a .funscript JSON file. Cleared when exporting publish JSON. Schema: string / default=.
allAxes (All Axes, ro): Available funscript channels including the TopLevel pseudo-axis. Schema: array[string].
selectedAxis (Selected Axis, rw): Axis id to load from the funscript file. Schema: string / default=TopLevel.
points (Points, ro): Normalized [timeSec, pos01] points loaded from the selected axis. Schema: array[array] / default=[].
maxT (Max T, rw): Loop period in seconds. Initialized from the funscript duration and user-overridable. Schema: number / default=10.0.
interp (Interp, rw): Interpolation method used for runtime output. Heatmap remains linear. Schema: string / enum[linear, pchip, akima, cubic_spline] / default=linear.
heatmap (Heatmap, ro): Per-time-bin activity heatmap derived from the selected axis. Schema: array[number] / default=[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0].
lastError (Last Error, ro): Last load or parse error. Schema: string / default=.
| Name |
Required |
On Node |
Schema |
Description |
t |
true |
true |
number |
Scalar time input in seconds. Runtime evaluation uses t % maxT. |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
value |
true |
true |
number |
Normalized output from the selected funscript axis using the chosen interpolation mode. |
- No bundled scenario references this node yet.
Detrend (f8.detrend)
Removes slow baseline or linear trend from scalar or vector inputs.
When to Use
- Use
Detrend when a signal has slow drift or baseline movement that should be removed before downstream analysis.
- It is useful ahead of envelope, periodicity, or threshold-based logic that should react to motion rather than offset.
- This operator works well for both scalar streams and small vectors.
Common Wiring Patterns
- Motion Isolation: Place
Detrend before Envelope or Range Map when a source slowly wanders over time.
- Pre-Filter Stage: Remove baseline drift before applying
Lowpass Filter, Highpass Filter, or periodicity analysis.
- State Reset Path: Use
reset_on_state_change when upstream mode changes would otherwise carry old baseline history forward.
Pitfalls / Gotchas
- Too Aggressive Alpha: Over-tuning the detrend state can erase intentional slow motion along with unwanted drift.
- History Reset: Resetting too often can produce abrupt jumps in the output.
- Expectation Gap:
Detrend removes baseline; it is not a substitute for smoothing or band-limiting.
Operator Reference
- Exec in ports: none
- Exec out ports: none
- Data inputs:
value
- Data outputs:
value
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
mode |
rw |
true |
true |
string / enum[CONSTANT, LINEAR] / default=CONSTANT |
Detrend mode. |
alpha |
rw |
true |
true |
number / default=0.05 |
Trend tracking smoothing factor. |
reset_on_state_change |
rw |
true |
false |
boolean / default=True |
Reset tracker history when parameters change. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
mode (Mode, rw): Detrend mode. Schema: string / enum[CONSTANT, LINEAR] / default=CONSTANT.
alpha (Alpha, rw): Trend tracking smoothing factor. Schema: number / default=0.05.
reset_on_state_change (Reset On State Change, rw): Reset tracker history when parameters change. Schema: boolean / default=True.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
| Name |
Required |
On Node |
Schema |
Description |
value |
true |
true |
any |
Value to detrend. |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
value |
true |
true |
any |
Detrended output. |
- No bundled scenario references this node yet.
Lowpass Filter (f8.lowpass_filter)
Butterworth IIR low-pass filter for scalar or vector inputs.
When to Use
- Use
Lowpass Filter to smooth noisy scalar or vector signals while preserving slower movement.
- It is a good fit when downstream control logic should ignore jitter or small high-frequency fluctuations.
- Reach for it when you want a more frequency-aware smoother than a simple EMA.
Common Wiring Patterns
- Noise Cleanup: Place it after pose-derived values, sensor streams, or expression outputs before mapping to hardware.
- Control Stabilization: Pair it with
Range Map and Rate Limiter to build a calmer control chain.
- Pre-Visualization: Filter a signal before plotting it in a wave view to see long-term movement more clearly.
Pitfalls / Gotchas
- Latency Tradeoff: Lower cutoffs reduce noise but also delay the response.
- Sample Interval Match: Keep
sampleIntervalMs aligned with the actual update cadence, or the filter shape will be misleading.
- Wrong Tool: Use
Highpass Filter or Bandpass Filter instead when you need to isolate faster motion components.
Operator Reference
- Exec in ports: none
- Exec out ports: none
- Data inputs:
value
- Data outputs:
value
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
sampleIntervalMs |
rw |
true |
true |
number / default=8.333333333333334 |
Sampling interval in milliseconds. |
cutoff |
rw |
true |
true |
number / default=8.0 |
Low-pass cutoff frequency in Hz. |
order |
rw |
true |
false |
number / default=2 |
Butterworth filter order. |
reset_on_state_change |
rw |
true |
false |
boolean / default=True |
Reset filter history when parameters change. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
sampleIntervalMs (Sample Interval (ms), rw): Sampling interval in milliseconds. Schema: number / default=8.333333333333334.
cutoff (Cutoff, rw): Low-pass cutoff frequency in Hz. Schema: number / default=8.0.
order (Order, rw): Butterworth filter order. Schema: number / default=2.
reset_on_state_change (Reset On State Change, rw): Reset filter history when parameters change. Schema: boolean / default=True.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
| Name |
Required |
On Node |
Schema |
Description |
value |
true |
true |
any |
Value to filter. |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
value |
true |
true |
any |
Filtered output. |
- No bundled scenario references this node yet.
Highpass Filter (f8.highpass_filter)
Butterworth IIR high-pass filter for scalar or vector inputs.
When to Use
- Use
Highpass Filter to remove slow movement and emphasize quicker changes in a signal.
- It is useful when downstream logic should react to impacts, pulses, or short-term motion rather than steady offsets.
- This operator is often a good companion to periodicity or onset-style analysis.
Common Wiring Patterns
- Pulse Extraction: Put it before an envelope or threshold detector to highlight transient motion.
- Drift Rejection: Use it after a slowly moving control source when only rapid changes should pass through.
- Motion Feature Branch: Split a signal into lowpass and highpass branches for separate visual or control purposes.
Pitfalls / Gotchas
- Cutoff Too High: Aggressive cutoffs can make the output feel thin or unstable.
- Sample Interval Match:
sampleIntervalMs must reflect the real update rate for the filter to behave as expected.
- Noise Boost: High-pass filtering can make small upstream jitter more visible, not less.
Operator Reference
- Exec in ports: none
- Exec out ports: none
- Data inputs:
value
- Data outputs:
value
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
sampleIntervalMs |
rw |
true |
true |
number / default=8.333333333333334 |
Sampling interval in milliseconds. |
cutoff |
rw |
true |
true |
number / default=1.0 |
High-pass cutoff frequency in Hz. |
order |
rw |
true |
false |
number / default=2 |
Butterworth filter order. |
reset_on_state_change |
rw |
true |
false |
boolean / default=True |
Reset filter history when parameters change. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
sampleIntervalMs (Sample Interval (ms), rw): Sampling interval in milliseconds. Schema: number / default=8.333333333333334.
cutoff (Cutoff, rw): High-pass cutoff frequency in Hz. Schema: number / default=1.0.
order (Order, rw): Butterworth filter order. Schema: number / default=2.
reset_on_state_change (Reset On State Change, rw): Reset filter history when parameters change. Schema: boolean / default=True.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
| Name |
Required |
On Node |
Schema |
Description |
value |
true |
true |
any |
Value to filter. |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
value |
true |
true |
any |
Filtered output. |
- No bundled scenario references this node yet.
Bandpass Filter (f8.bandpass_filter)
Butterworth IIR band-pass filter for scalar or vector inputs.
When to Use
- Use
Bandpass Filter when only a middle frequency range is interesting and both slow drift and fast noise should be suppressed.
- It is helpful for isolating rhythmic motion or a known movement band.
- This is the right choice when lowpass alone is too broad and highpass alone is too noisy.
Common Wiring Patterns
- Rhythm Isolation: Feed pose- or audio-derived control signals through
Bandpass Filter before periodicity or feature extraction.
- Signal Cleanup: Use it before
Envelope when the target motion lives in a narrower band than the raw stream.
- Parallel Analysis: Compare unfiltered, lowpass, and bandpass branches side by side in wave visualization while tuning.
Pitfalls / Gotchas
- Bad Cutoff Ordering:
low_cutoff must stay below high_cutoff, and both should make sense for the source cadence.
- Over-Narrow Window: A very tight band can ring or remove the motion you actually care about.
- Cadence Sensitivity: Incorrect
sampleIntervalMs settings distort the effective pass band.
Operator Reference
- Exec in ports: none
- Exec out ports: none
- Data inputs:
value
- Data outputs:
value
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
sampleIntervalMs |
rw |
true |
true |
number / default=8.333333333333334 |
Sampling interval in milliseconds. |
low_cutoff |
rw |
true |
true |
number / default=1.0 |
Lower band edge in Hz. |
high_cutoff |
rw |
true |
true |
number / default=8.0 |
Upper band edge in Hz. |
order |
rw |
true |
false |
number / default=2 |
Butterworth filter order. |
reset_on_state_change |
rw |
true |
false |
boolean / default=True |
Reset filter history when parameters change. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
sampleIntervalMs (Sample Interval (ms), rw): Sampling interval in milliseconds. Schema: number / default=8.333333333333334.
low_cutoff (Low Cutoff, rw): Lower band edge in Hz. Schema: number / default=1.0.
high_cutoff (High Cutoff, rw): Upper band edge in Hz. Schema: number / default=8.0.
order (Order, rw): Butterworth filter order. Schema: number / default=2.
reset_on_state_change (Reset On State Change, rw): Reset filter history when parameters change. Schema: boolean / default=True.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
| Name |
Required |
On Node |
Schema |
Description |
value |
true |
true |
any |
Value to filter. |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
value |
true |
true |
any |
Filtered output. |
- No bundled scenario references this node yet.
Periodicity Detector (f8.periodicity_detector)
Detects whether a scalar signal is periodic using short-time autocorrelation peaks.
When to Use
- Use
Periodicity Detector to estimate whether a scalar signal contains a stable repeating pattern.
- It is useful for motion quality checks, rhythm detection, or gating logic based on periodic confidence.
- This operator works best when the upstream signal already represents a meaningful one-dimensional feature.
Common Wiring Patterns
- Periodic Motion Gate: Feed
is_periodic or confidence into downstream state or exec logic to enable outputs only during stable repetition.
- Tempo/Period Probe: Use
periodMs or period_hz to inspect the dominant rhythm of an input stream.
- Feature Stack: Place it after detrending and filtering so the detector sees a cleaner signal.
Pitfalls / Gotchas
- Garbage In: Raw noisy inputs usually need filtering first, or confidence will be erratic.
- Window Tuning:
window, min_lag, and max_lag strongly affect what periods can be detected.
- Confidence Semantics: High confidence means repeatability, not necessarily high amplitude or good control quality.
Operator Reference
- Exec in ports: none
- Exec out ports: none
- Data inputs:
value
- Data outputs:
confidence, rms, periodicEnergy, periodMs, period_hz, is_periodic
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
window |
rw |
true |
false |
number / default=150 |
Autocorrelation history window in samples. |
min_lag |
rw |
true |
false |
number / default=10 |
Minimum lag to scan for periodic peaks. |
max_lag |
rw |
true |
false |
number / default=150 |
Maximum lag to scan for periodic peaks. |
peak_prominence |
rw |
true |
false |
number / default=0.1 |
Minimum local prominence for a valid autocorrelation peak. |
min_peaks |
rw |
true |
false |
number / default=1 |
Minimum number of valid peaks before full confidence. |
smoothing_alpha |
rw |
true |
false |
number / default=0.25 |
EMA smoothing factor applied to confidence. |
noise_floor |
rw |
true |
false |
number / default=0.0001 |
Minimum centered energy before confidence can rise. |
threshold |
rw |
true |
true |
number / default=0.6 |
Decision threshold for the boolean periodic output. |
rms_window |
rw |
true |
false |
number / default=64 |
RMS window length in samples. |
sampleIntervalMs |
rw |
true |
true |
number / default=33.3333333333 |
Sampling interval in milliseconds used to convert detected period into frequency. |
reset_on_missing |
rw |
true |
false |
boolean / default=False |
Decay confidence when the input is missing. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
window (Window, rw): Autocorrelation history window in samples. Schema: number / default=150.
min_lag (Min Lag, rw): Minimum lag to scan for periodic peaks. Schema: number / default=10.
max_lag (Max Lag, rw): Maximum lag to scan for periodic peaks. Schema: number / default=150.
peak_prominence (Peak Prominence, rw): Minimum local prominence for a valid autocorrelation peak. Schema: number / default=0.1.
min_peaks (Min Peaks, rw): Minimum number of valid peaks before full confidence. Schema: number / default=1.
smoothing_alpha (Smoothing Alpha, rw): EMA smoothing factor applied to confidence. Schema: number / default=0.25.
noise_floor (Noise Floor, rw): Minimum centered energy before confidence can rise. Schema: number / default=0.0001.
threshold (Threshold, rw): Decision threshold for the boolean periodic output. Schema: number / default=0.6.
| Name |
Required |
On Node |
Schema |
Description |
value |
true |
true |
number |
Scalar signal input. |
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
confidence |
true |
true |
number |
Autocorrelation periodicity confidence (0..1). |
rms |
true |
true |
number |
Short-term RMS envelope. |
periodicEnergy |
true |
true |
number |
RMS multiplied by periodicity confidence. |
periodMs |
true |
true |
number |
Detected dominant period in milliseconds. |
period_hz |
true |
true |
number |
Detected dominant frequency in Hz. |
is_periodic |
true |
true |
boolean |
True when confidence exceeds threshold. |
- No bundled scenario references this node yet.
Recorder (f8.recorder)
Tick-driven debug recorder that captures data samples and sparse state changes.
When to Use
- Use
Recorder to capture debug samples and sparse state changes from a running graph.
- It is intended for inspection, reproducibility, and offline analysis rather than production data logging at arbitrary scale.
- This node is especially helpful when you need to compare live behavior against later replays.
Common Wiring Patterns
- Debug Capture: Trigger recording during a tuning session, then inspect the saved data after reproducing a problem.
- Regression Fixture Builder: Capture representative sessions that can later be replayed through
Replayer.
- Selective Session Logging: Toggle
enabled or recording around the specific time window you care about.
Pitfalls / Gotchas
- Path Management: Make sure
path points somewhere writable and predictable for your environment.
- Not a Telemetry Stack: This node is for targeted debug capture, not long-running archival logging.
- No Live Counters In State: Recorded sample/event counters are kept internally and in the recording file, not published as state, so a high-frequency recording session does not flood Studio state sync.
- Scope Awareness: Record only the samples you need, or the captured session becomes harder to reason about.
Operator Reference
- Exec in ports:
record
- Exec out ports: none
- Exec inputs:
record
- Data inputs: none
- Data outputs: none
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
path |
rw |
true |
true |
string |
Recording output path. |
enabled |
rw |
true |
true |
boolean / default=True |
When enabled, incoming exec ticks are recorded. |
append |
rw |
true |
true |
boolean / default=True |
Append to an existing compatible recording file. |
recording |
ro |
true |
true |
boolean / default=False |
Readonly flag indicating whether the file is open and writable. |
sessionStartTsMs |
ro |
true |
false |
integer |
Readonly session start timestamp in milliseconds. |
lastError |
ro |
true |
true |
string |
Last recording error message. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
path (Path, rw): Recording output path. Schema: string.
enabled (Enabled, rw): When enabled, incoming exec ticks are recorded. Schema: boolean / default=True.
append (Append, rw): Append to an existing compatible recording file. Schema: boolean / default=True.
recording (Recording, ro): Readonly flag indicating whether the file is open and writable. Schema: boolean / default=False.
sessionStartTsMs (Session Start, ro): Readonly session start timestamp in milliseconds. Schema: integer.
lastError (Last Error, ro): Last recording error message. Schema: string.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
operatorId (Operator Id, ro): Readonly: current operator/node id (operatorId). Schema: string.
None
Data Output Ports
None
- No bundled scenario references this node yet.
Replayer (f8.replayer)
Playback recorded data and sparse state changes for debugging.
When to Use
- Use
Replayer to play back data captured by Recorder for debugging and repeatable iteration.
- It is useful when you want to validate graph behavior against a known session without needing the live upstream source.
- This node helps turn intermittent runtime issues into deterministic repro cases.
Common Wiring Patterns
- Offline Repro: Load a capture file and drive downstream logic from
sample events while live inputs stay disconnected.
- A/B Graph Tuning: Replay the same session repeatedly while adjusting filters, mappings, or thresholds.
- Looped Demo Source: Enable
loop for repeated playback during UI or behavior tuning.
Pitfalls / Gotchas
- File Compatibility:
Replayer expects the recorder output format; arbitrary files will not work.
- Time Mode Choice: Check
timeMode before judging behavior, since playback pacing affects the graph feel.
- False Confidence: Replayed sessions are great for regression checks, but they do not replace live end-to-end validation.
Operator Reference
- Exec in ports:
play, pause, stop
- Exec out ports:
sample, started, stopped, looped, done
- Exec inputs:
play, pause, stop
- Exec outputs:
sample, started, stopped, looped, done
- Data inputs: none
- Data outputs:
positionMs
State Fields
| Name |
Access |
Required |
On Node |
Schema |
Description |
path |
rw |
true |
true |
string |
Recording file path. |
loop |
rw |
true |
true |
boolean / default=False |
Loop when playback reaches the end. |
timeMode |
rw |
true |
true |
string / enum[recorded_epoch, offset_from_play] / default=offset_from_play |
Playback time mapping mode. |
playing |
rw |
true |
true |
boolean / default=False |
Whether playback is currently running. |
durationMs |
ro |
true |
true |
integer / default=0 |
Readonly recording duration in milliseconds. |
loaded |
ro |
true |
true |
boolean / default=False |
Readonly flag indicating whether the recording is loaded. |
lastError |
ro |
true |
true |
string |
Last playback error message. |
svcId |
ro |
true |
false |
string |
Readonly: current service instance id (svcId). |
operatorId |
ro |
true |
false |
string |
Readonly: current operator/node id (operatorId). |
Key Fields That Matter
path (Path, rw): Recording file path. Schema: string.
loop (Loop, rw): Loop when playback reaches the end. Schema: boolean / default=False.
timeMode (Time Mode, rw): Playback time mapping mode. Schema: string / enum[recorded_epoch, offset_from_play] / default=offset_from_play.
playing (Playing, rw): Whether playback is currently running. Schema: boolean / default=False.
durationMs (Duration, ro): Readonly recording duration in milliseconds. Schema: integer / default=0.
loaded (Loaded, ro): Readonly flag indicating whether the recording is loaded. Schema: boolean / default=False.
lastError (Last Error, ro): Last playback error message. Schema: string.
svcId (Service Id, ro): Readonly: current service instance id (svcId). Schema: string.
None
Data Output Ports
| Name |
Required |
On Node |
Schema |
Description |
positionMs |
false |
true |
integer / default=0 |
Current playback position in milliseconds. |
- No bundled scenario references this node yet.