Path Effect Scripts are one of the more advanced features in Rive, and they open up a lot of creative possibilities.
In this tutorial, we'll use them to build a dynamic, animated brush effect - the kind of rough, hand-drawn shape you might use behind a UI label or tag.
The goal isn't to create a "perfect" brush. It's to show how a small script can evolve into something flexible, reusable, and animated - something you can keep refining and adapting to your own projects.
Starting With a Simple Shape
We begin with a basic setup: a rectangle used as a background shape behind a text label.
I used Rive's Layouts feature to make sure the rectangle always resizes to fit the text with a bit of padding.
At this stage, the shape is static. No animation, no logic - just a clean, responsive starting point.
From here, we'll gradually add complexity using scripting.
Creating the First Path Effect Script
To start working with scripting, we create a Path Effect Script and apply it to the shape's Fill layer.
At first, the shape disappears - and that's expected.
Why? Because the script's update() function returns a new path, and until we define its behavior, that path is empty.
This is actually a good sign. It tells us the script is wired correctly.
Verifying the Script Works
Before building anything complex, it's worth confirming the pipeline works.
Instead of writing logic manually, we ask the Agent to slightly distort the shape along the Y-axis - just enough to see a visible change.
Prompt - Basic distortion test:
Use the existing Script
Modify the path in update() so that all points are slightly offset
on the Y axis using a simple sine function.
Do not use time. Keep the result deterministic
Use the existing Script
Modify the path in update() so that all points are slightly offset
on the Y axis using a simple sine function.
Do not use time. Keep the result deterministic
Use the existing Script
Modify the path in update() so that all points are slightly offset
on the Y axis using a simple sine function.
Do not use time. Keep the result deterministic
Once applied, the shape shows a subtle distortion. Nothing fancy - just confirmation that the script is running and affecting the geometry.
After that, we remove this temporary logic and start fresh.
What Makes a Brush Feel Hand-Drawn?
A hand-drawn brush isn't about perfection.
It's defined by:
Slightly uneven edges
Inconsistent thickness
Small variations that make it feel organic, not mathematical
To simulate that imperfection - and make it dynamic - we introduce three core properties.
Core Brush Properties
Amplitude
Controls how strong the distortion is.
Low values = subtle, soft distortion
High values = bold, aggressive distortion
You can think of this as how much you allow yourself to "break" the shape.
Frequency
Controls how often the distortion appears along the shape.
Low values = wide, loose variations
High values = dense, tightly packed variations
Seed
Introduces variation.
Changing the seed gives you different versions of the same brush, preventing everything from looking identical.
Generating the Brush
Instead of implementing all of this manually, we use the Agent again - this time with a more intentional prompt.
We ask it to use the existing script, implement the three properties, keep the distortion soft and UI-friendly, and include sensible default values.
Prompt - Brush generation with Amplitude / Frequency / Seed:
Create a Path Effect Script that produces a static, organic brush-like edge
for a rectangular shape used in UI.
The goal is to gently break the perfect geometry of the shape
so it feels hand-drawn, but still clean, readable, and suitable for UI.
Apply smooth, continuous distortion along all four edges
(top, bottom, left, and right),
with rounded, flowing curves rather than sharp angles.
The distortion should:
- Be strongest near the center of each edge
- Gradually fade out toward the corners
- Keep corners soft and stable
- Avoid sharp spikes, jagged shapes, or visible teeth
Expose exactly three input parameters:
- amplitude (range: 0–50, default: 10)
Controls how strong the distortion is.
Low values should be subtle and restrained,
higher values should feel more hand-drawn but still controlled.
- frequency (range: 0–50, default: 6)
Controls how often the distortion appears along the edges.
Favor low-frequency, rounded variations.
Avoid aggressive or noisy patterns.
- seed (range: 0–50, default: 0)
Controls deterministic variation of the distortion pattern.
Changing the seed should slightly shift the brush look
while keeping the result stable and repeatable.
Keep the effect static and deterministic.
Do not use time or animation.
Geometry changes should occur only in update()
Create a Path Effect Script that produces a static, organic brush-like edge
for a rectangular shape used in UI.
The goal is to gently break the perfect geometry of the shape
so it feels hand-drawn, but still clean, readable, and suitable for UI.
Apply smooth, continuous distortion along all four edges
(top, bottom, left, and right),
with rounded, flowing curves rather than sharp angles.
The distortion should:
- Be strongest near the center of each edge
- Gradually fade out toward the corners
- Keep corners soft and stable
- Avoid sharp spikes, jagged shapes, or visible teeth
Expose exactly three input parameters:
- amplitude (range: 0–50, default: 10)
Controls how strong the distortion is.
Low values should be subtle and restrained,
higher values should feel more hand-drawn but still controlled.
- frequency (range: 0–50, default: 6)
Controls how often the distortion appears along the edges.
Favor low-frequency, rounded variations.
Avoid aggressive or noisy patterns.
- seed (range: 0–50, default: 0)
Controls deterministic variation of the distortion pattern.
Changing the seed should slightly shift the brush look
while keeping the result stable and repeatable.
Keep the effect static and deterministic.
Do not use time or animation.
Geometry changes should occur only in update()
Create a Path Effect Script that produces a static, organic brush-like edge
for a rectangular shape used in UI.
The goal is to gently break the perfect geometry of the shape
so it feels hand-drawn, but still clean, readable, and suitable for UI.
Apply smooth, continuous distortion along all four edges
(top, bottom, left, and right),
with rounded, flowing curves rather than sharp angles.
The distortion should:
- Be strongest near the center of each edge
- Gradually fade out toward the corners
- Keep corners soft and stable
- Avoid sharp spikes, jagged shapes, or visible teeth
Expose exactly three input parameters:
- amplitude (range: 0–50, default: 10)
Controls how strong the distortion is.
Low values should be subtle and restrained,
higher values should feel more hand-drawn but still controlled.
- frequency (range: 0–50, default: 6)
Controls how often the distortion appears along the edges.
Favor low-frequency, rounded variations.
Avoid aggressive or noisy patterns.
- seed (range: 0–50, default: 0)
Controls deterministic variation of the distortion pattern.
Changing the seed should slightly shift the brush look
while keeping the result stable and repeatable.
Keep the effect static and deterministic.
Do not use time or animation.
Geometry changes should occur only in update()
Adding Direction for a More Natural Feel
Real brush strokes are rarely symmetrical. They usually have a direction - top to bottom or left to right.
To capture that, we add two more properties:
Direction
Defines the dominant stroke direction:
0 = no dominant direction
1 = vertical stroke (top to bottom)
2 = horizontal stroke (left to right)
The distortion becomes stronger perpendicular to the stroke direction.
Direction Strength
Controls how noticeable that directional difference is:
Low values = very subtle
High values = clearly directional
Together, these properties make the brush feel more natural and intentional.
Prompt - Adding Direction and Direction strength
Extend the existing brush-like path distortion
by adding directional bias to break symmetry.
Add two new input parameters:
- direction (number, default: 0)
Controls the dominant distortion direction:
0 = uniform (all edges behave equally)
1 = horizontal bias (top and bottom edges distort more,
left and right edges distort less)
2 = vertical bias (left and right edges distort more,
top and bottom edges distort less)
- directionStrength (range: 0–10, default: 4)
Controls how strong the directional bias is.
Low values should be subtle,
higher values should clearly emphasize the chosen direction.
Apply the directional bias smoothly and gradually,
without creating sharp transitions or breaking the shape.
The goal is to make the brush feel more hand-drawn
by introducing controlled asymmetry,
while keeping the result clean and suitable for UI
Extend the existing brush-like path distortion
by adding directional bias to break symmetry.
Add two new input parameters:
- direction (number, default: 0)
Controls the dominant distortion direction:
0 = uniform (all edges behave equally)
1 = horizontal bias (top and bottom edges distort more,
left and right edges distort less)
2 = vertical bias (left and right edges distort more,
top and bottom edges distort less)
- directionStrength (range: 0–10, default: 4)
Controls how strong the directional bias is.
Low values should be subtle,
higher values should clearly emphasize the chosen direction.
Apply the directional bias smoothly and gradually,
without creating sharp transitions or breaking the shape.
The goal is to make the brush feel more hand-drawn
by introducing controlled asymmetry,
while keeping the result clean and suitable for UI
Extend the existing brush-like path distortion
by adding directional bias to break symmetry.
Add two new input parameters:
- direction (number, default: 0)
Controls the dominant distortion direction:
0 = uniform (all edges behave equally)
1 = horizontal bias (top and bottom edges distort more,
left and right edges distort less)
2 = vertical bias (left and right edges distort more,
top and bottom edges distort less)
- directionStrength (range: 0–10, default: 4)
Controls how strong the directional bias is.
Low values should be subtle,
higher values should clearly emphasize the chosen direction.
Apply the directional bias smoothly and gradually,
without creating sharp transitions or breaking the shape.
The goal is to make the brush feel more hand-drawn
by introducing controlled asymmetry,
while keeping the result clean and suitable for UI
Adding Animation (Stop Motion Style)
At this point, we have a solid brush effect. We could keep refining it - adding more properties and logic - but instead, we move on to animation.
Because this brush is meant to feel rough and hand-made, a stop-motion style animation works well.
Stop motion uses small, stepped movements, creating a slightly jittery, imperfect motion that complements the brush aesthetic.
Prompt - Animation logic:
Extend the existing brush-like path distortion
by adding optional stop-motion style animation.
Add two new input parameters:
- animate (boolean, default: false)
Enables or disables animation.
- animationSpeed (number, default: 0.5)
Controls how often the distortion updates.
Lower values should result in slower, subtler motion,
higher values in faster, more noticeable changes.
When animation is enabled:
- Update the distortion only at discrete time intervals
(not every frame) to create a stop-motion feel.
- Slightly offset the existing distortion pattern
(for example by shifting the seed)
rather than generating a completely new shape.
- Keep each frame stable until the next update.
When animation is disabled:
- The brush should remain completely static and deterministic.
The animation should feel subtle, hand-drawn,
and suitable for UI — not fluid or noisy
Extend the existing brush-like path distortion
by adding optional stop-motion style animation.
Add two new input parameters:
- animate (boolean, default: false)
Enables or disables animation.
- animationSpeed (number, default: 0.5)
Controls how often the distortion updates.
Lower values should result in slower, subtler motion,
higher values in faster, more noticeable changes.
When animation is enabled:
- Update the distortion only at discrete time intervals
(not every frame) to create a stop-motion feel.
- Slightly offset the existing distortion pattern
(for example by shifting the seed)
rather than generating a completely new shape.
- Keep each frame stable until the next update.
When animation is disabled:
- The brush should remain completely static and deterministic.
The animation should feel subtle, hand-drawn,
and suitable for UI — not fluid or noisy
Extend the existing brush-like path distortion
by adding optional stop-motion style animation.
Add two new input parameters:
- animate (boolean, default: false)
Enables or disables animation.
- animationSpeed (number, default: 0.5)
Controls how often the distortion updates.
Lower values should result in slower, subtler motion,
higher values in faster, more noticeable changes.
When animation is enabled:
- Update the distortion only at discrete time intervals
(not every frame) to create a stop-motion feel.
- Slightly offset the existing distortion pattern
(for example by shifting the seed)
rather than generating a completely new shape.
- Keep each frame stable until the next update.
When animation is disabled:
- The brush should remain completely static and deterministic.
The animation should feel subtle, hand-drawn,
and suitable for UI — not fluid or noisy
Once applied, we can enable animation, adjust speed live, and see the effect update in real time.
If the animation feels too slow, a simple follow-up works:
Can you make the animation faster
Can you make the animation faster
Can you make the animation faster
Using the Brush in Real UI
Finally, we can turn this effect into something reusable.
The brush becomes a component, connected to Data Binding, and reused across multiple UI elements - for example, "New" and "On Sale" labels.
The same script drives:
Shape distortion
Animation
Responsiveness
Visual consistency
This is where scripting really shines. A small piece of logic becomes a reusable, expressive UI system.
Wrapping Up
Path Effect Scripts can turn simple shapes into dynamic, animated, and reusable UI elements.
The brush we built isn't "final" - and that's the point. You can keep refining it, extending it, and adapting it to your own needs.