Skip to content
Kapow
Esc
↑↓navigate↵open⌘Jpreview
On this page

Change: glitch

An element splits into offset color channels and shifted slices for a moment when a value changes.

Glitch makes the element look like a broken signal for a moment: its red, green and blue channels pull apart, horizontal slices jump sideways, and then everything snaps back. Use it for an error that repeats, a failed payment or a rejected code, where a shake would be too familiar.

Usage

The sample glitches an error toast each time another attempt fails.

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import com.jantursky.kapow.ChangeEffect
import com.jantursky.kapow.changeEffect

@Composable
fun ErrorToast(failures: Int) {
    BasicText(
        text = "Payment failed. Try again.",
        style = TextStyle(color = Color.White),
        modifier = Modifier
            .changeEffect(failures, ChangeEffect.glitch(offset = 8.dp))
            .clip(RoundedCornerShape(10.dp))
            .background(Color(0xFF161616))
            .padding(horizontal = 16.dp, vertical = 12.dp),
    )
}

Parameters

Parameter Default Description
duration 300.milliseconds How long the glitch lasts. Must be positive.
offset 6.dp How far the color channels split apart. Slices jump up to one and a half times as far. Must not be negative; 0 keeps the element in place and only redraws it.
sliceHeight 0.08f..0.3f The range a slice’s height is picked from, as a fraction of the element’s height. Taller slices mean fewer of them.
channelColors listOf(Color.Red, Color.Green, Color.Blue) The colors of the three copies of the element. The first goes one way, the third the other and the second stays. Each copy keeps one channel of the element (red, green, blue in turn) and tints it in its color, so the default rebuilds the original. Must have three colors.
jumpChance 0.5f The share of slices that jump sideways, from 0 to 1.
jitterRate 20f How many times a second the glitch changes its look. Must be positive.
seed 0 The first seed. Each change takes the next one, so the same seed gives the same glitches.

Reduced motion

Nothing splits or shifts. The element brightens briefly. With reducedMotion(ReducedMotionMode.EndState) nothing shows at all, since the effect ends at rest.

Notes

  • The look changes every 50 ms: a new split and a new set of slices, about half of which jump. The pattern comes from a seed, so it is the same on every run and every platform.
  • The element is drawn once into a layer of its own, and that layer is drawn again for each slice and channel. The copies are added together in an offscreen buffer, which rebuilds the original wherever they line up. No shader is involved, so it works on Android 6.
  • Nothing is drawn outside the element’s bounds: slices that jump are cut off at its edges, and the gaps they leave show what is behind the element.
  • Semi-transparent edges, such as anti-aliased text on a transparent background, can look slightly darker while the glitch runs.
  • There is no cooldown. A change during a glitch starts a new one.

Flash safety

glitch changes its look up to 20 times a second, so it is a flashing effect. With LocalKapowMaxFlashesPerSecond set to N, it changes its look at most N times a second, and a change that comes less than 1/N seconds after the last play is ignored, never queued. At 3 the glitch becomes a slow stutter; use reducedMotion(ReducedMotionMode.Fade) if that is not what you want. Off by default. The limit is a cap on the rate, not a claim of WCAG 2.3.1 conformance. See the accessibility guide.

Live example

Was this page helpful?