Characters remaining

Matt Perry
In this tutorial, we're going to build the Characters remaining example step-by-step.
This example is rated intermediate difficulty, which means we'll spend some time explaining the Motion APIs we've chosen to use, but it assumes familiarity with JavaScript as a language.
Here's a live demo of the example we're going to be creating:
Introduction
The Characters remaining example shows how to build an interactive character counter for a text input that provides visual feedback as users approach the character limit. The counter changes color and bounces with spring physics when there are only a few characters left.
This tutorial uses the animate function to trigger spring animations and the transform utility to map values between ranges. Motion makes it easy to create these kinds of responsive, physics-based interactions that feel natural and engaging.
Get started
Let's start by creating the HTML structure for the input field and character counter:
The counter sits inside a div that's positioned absolutely over the right side of the input. We use a gradient background so the counter text remains readable as you type. The initial counter value is set to 12, which matches our maximum character limit.
Let's animate!
Import from Motion
First, we'll import the functions we need from Motion:
The animate function lets us trigger animations on demand, while transform helps us convert values from one range to another.
Create transform functions
Before we add the event handler, we'll create two transform functions that will map the remaining character count to visual properties:
The transform function takes two arrays - an input range and an output range - and returns a function that maps values between them.
The first transform maps characters remaining from 2 to 6 onto colors from pink (#ff008c) to gray (#ccc). When there are 6 characters left, the counter will be gray. As you continue typing and only 2 characters remain, it transitions to pink.
The second transform maps characters remaining from 0 to 5 onto spring velocities from 50 to 0. When you're close to the limit, the bounce will be more energetic. This creates an escalating sense of urgency as the counter approaches zero.
We create these functions outside the event handler for performance - there's no need to recreate them on every keystroke.
Add willChange optimization
Before animating, we'll optimize for performance:
Setting willChange to "transform" tells the browser that we'll be animating this property, allowing it to optimize rendering. This is especially important for animations that run frequently, like our character counter.
Update the counter on input
Now we'll add an event listener that updates the counter and triggers animations:
Every time the user types, we calculate how many characters are remaining and update the counter text. Then we use our mapRemainingToColor transform to update the color based on the remaining count.
When there are 6 or fewer characters left, we trigger a spring animation. We're animating to scale: 1, which might seem odd since that's the element's default scale. But here's the key: we're specifying a spring velocity using our mapRemainingToSpringVelocity transform. This creates a bounce effect even though we're animating to the current state.
Think of it like pulling back a spring and releasing it - even if it ends up in the same position, the journey there creates the bounce. The closer you get to the character limit, the higher the velocity, making the bounce more dramatic.
The spring configuration uses high stiffness (700) for a snappy bounce and moderate damping (80) to let it oscillate a bit before settling.
Conclusion
We've built an interactive character counter that uses Motion's animate function and transform utility to create engaging visual feedback. The example shows how small touches of animation can make forms feel more alive and responsive. By mapping the remaining character count to both color and spring velocity, we create an intuitive sense of urgency that guides users without being intrusive.


