Core Runner¶
The core runner stores named feedback recipes, plays named or inline sequences, advances active work through feel.update(dt), and leaves rendering or side effects to your app.
Targets¶
Use feel.target(meta) when a sequence needs animated values.
local button = feel.target({
label = "Launch",
values = { x = 0, y = 0, scale = 1, opacity = 1, glow = 0 },
})
The default transform fields are opacity, x, y, scale, scaleX, scaleY, and rotation. You can also add any numeric field your game wants to animate, such as glow, shake, teleportGlow, or charge.
Metadata that is not inside values stays available on the target table, but it is not tweened:
Named Sequences¶
Use feel.define(name, sequence) for reusable recipes.
feel.define("button.press", {
{ kind = "audio", cue = "press" },
{ kind = "animate", duration = 0.06, to = { scale = 0.92 }, ease = "quadout" },
{ kind = "animate", duration = 0.16, to = { scale = 1 }, ease = "backout" },
})
feel.get(name) returns the normalized sequence.
Use feel.validate(sequence) while authoring or loading recipes to catch common shape problems before playback:
Playback¶
feel.play(nameOrSequence, target, opts) accepts a named sequence or inline sequence.
feel.play("button.press", button, {
trigger = "click",
restart = true,
key = "button.press",
emit = function(event, ctx) end,
audio = function(event, ctx) end,
markDirty = function(ctx) end,
})
target is optional for event-only recipes. If an animation step runs without a target, the runner creates an internal target.
Use restart = true when a new play should cancel the previous active play in the same target/key slot. Named sequences can omit key; inline sequences should pass a stable string key.
Without restart, repeated plays stack:
With restart, the second play replaces the first active run for that target/key:
feel.play("button.press", button, { restart = true })
feel.play("button.press", button, { restart = true })
Feedback Channels¶
Use feel.channel() when gameplay modules should announce feedback intents without importing the sequence module that handles them.
local feedback = feel.channel()
feedback:on("ship.explode", function(event)
feel.play("ship.explode", event.target, { restart = true, key = "ship.explode" })
end)
feedback:emit("ship.explode", { target = ship.target })
Channels are local objects. Create them where they make module boundaries cleaner, and keep gameplay state changes direct.
Update And Clear¶
Call feel.update(dt) once per frame. It advances tweens, waits, nested sequences, repeat loops, and parallel branches.
Use feel.active() and feel.isPlaying(target, key) as tiny debug helpers when restart slots, long waits, or nested sequences are hard to reason about:
for _, run in ipairs(feel.active()) do
print(run.source, run.key, run.index, run.count, run.remaining)
end
if feel.isPlaying(ship.target, "ship.teleport") then
print("teleport feedback is still active")
end
Use feel.clear() to clear all named sequences and active work. Use feel.clear(target) to stop active tweens and active sequences for one target.
Related Pages¶
- Sequence Steps describes every step shape.
- API lists function signatures and option fields.
- LOVE Adapter shows how emitted events become LOVE side effects.