Tuning effects
Shape particles with ParticlePhysics and EmissionArea, make randomness reproducible with seeds, set easing and brushes, and wrap any effect to change its timing and reduced-motion behavior.
Every effect has a default look. Since 1.0.0 each one also takes options to change it. This page covers the options that work the same way across effects.
Defaults keep the 1.0.0 look
Every new parameter has a default that reproduces what the effect did in 1.0.0. Code that compiled against 1.0.0 still compiles and looks the same. Old signatures stay in the binary, so a library built against 1.0.0 keeps working with the new version. You change a parameter only when you want a different look.
Particle physics
Effects that emit particles take a physics parameter: spray, confettiPop, hearts and fireworks on change effects, smoke, bubbles, flame and confettiRain on conditional effects, and KapowEnter.pop. ParticlePhysics.of(...) sets the launch and motion of the particles. Every part is optional, and a part you leave out keeps the effect’s own value. ParticlePhysics.Default changes nothing.
| Part | Meaning |
|---|---|
direction, spread |
The launch heading and the width of the cone, in degrees clockwise from pointing right. -90 is straight up. |
speed, lifetime |
A range each particle picks from: its launch speed and how long it lives. |
gravity, drag |
Pull downward and the braking of the particle’s speed. |
size, spin |
A range for the particle’s size and its turning speed. |
startScale, endScale |
The size factor at the start and at the end of a particle’s life. |
fade |
An easing that maps life progress to how far the particle has faded. |
emission |
Where particles start. See below. |
blendMode, colorPick |
How particles blend, and whether they take colors in order or at random. |
An effect ignores the parts it has no use for. The effect’s page says which ones it reads.
Emission area
EmissionArea decides where particles start, centered on the effect’s own origin and sized to the element:
EmissionArea.Pointstarts every particle at the origin.EmissionArea.Edge(side)spreads them along one edge.EdgeSideisTop,Bottom,StartorEnd, andStartandEndfollow the layout direction.EmissionArea.Rectstarts them anywhere inside the element’s bounds.
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.jantursky.kapow.ChangeEffect
import com.jantursky.kapow.EdgeSide
import com.jantursky.kapow.EmissionArea
import com.jantursky.kapow.ParticlePhysics
import com.jantursky.kapow.changeEffect
@Composable
fun Score(score: Int) {
BasicText(
text = "$score",
modifier = Modifier.changeEffect(
score,
ChangeEffect.spray(
count = 18,
physics = ParticlePhysics.of(
direction = -90f,
spread = 40f,
gravity = 600f,
emission = EmissionArea.Edge(EdgeSide.Top),
),
),
),
)
}
Create the ParticlePhysics once if you reuse it, for example in a remember block.
Seeds
Effects with random parts take a seed: the scatter of spray, confettiPop, hearts, fireworks, burst, speedLines, sparkle, stamp and glitch, and the conditional smoke, bubbles, flame, sparkles, confettiRain and repeat (for its jitter). Transitions with a random pattern take one too: dissolve, checker, blob and shatter, and KapowTypewriter for its scrambled glyphs.
The same seed gives the same pattern every time and on every platform. The default is 0 for the effects, and a fixed value for the transitions. For a change effect, each change takes the next seed in turn, so the sprays differ from one another but the whole sequence is the same on every run. Use a fixed seed in screenshot tests and previews. Leave it alone otherwise.
Easing and brushes
Many effects take an easing. It shapes how the effect moves over its duration: shine, pulse, spin, ripple, speedLines, trace, strike, rainbowBorder and KapowCounter have one, and KapowEnter and KapowExit take it through .easing(...). Pass any Compose Easing, such as FastOutSlowInEasing or a CubicBezierEasing. ProgressEffect.curve(easing) does the same for a progress effect.
Effects that draw an outline or a ring take a brush instead of a plain color: pulse, trace, strike, borderBeam, marchingAnts and the progress trace. A Brush such as a linear or sweep gradient replaces color. Effects that blend with the element, such as shine, flash and shimmer, take a blendMode.
import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import com.jantursky.kapow.ChangeEffect
import com.jantursky.kapow.changeEffect
@Composable
fun Highlight(count: Int) {
BasicText(
text = "$count",
modifier = Modifier.changeEffect(
count,
ChangeEffect.trace(
brush = Brush.sweepGradient(listOf(Color.Magenta, Color.Cyan, Color.Magenta)),
easing = CubicBezierEasing(0.2f, 0f, 0f, 1f),
),
),
)
}
Wrappers
Every effect type has wrappers that change when and how it plays. Each returns a new effect, so they chain. They are the same on every effect of a type:
| Type | Wrappers |
|---|---|
ChangeEffect |
delay, cooldown, times, speed, reducedMotion |
ConditionalEffect |
fadeIn, fadeOut, startDelay, speed, reducedMotion |
ProgressEffect |
curve, smoothed, reducedMotion |
KapowEnter, KapowExit |
delay, easing, reducedMotion |
The guides explain them: change effects, conditional effects, progress effects and transitions. reducedMotion(mode) takes a ReducedMotionMode and is covered in Reduced motion. KapowStagger.delay(index) builds delays for the items of a list, as Stagger shows.
Effects are compared by value, wrappers included, so building one inline in a composable is fine. A lambda such as rise(label = ...) is compared by identity: remember it, or a new lambda makes the effect unequal each time.