Move your mouse across the page and the character looks toward your cursor. Click, and the character does something. Scroll away, and it waves goodbye.
All four authors on bestaiweb.ai now react this way: Mona, Dan, Max and Alan.
Mona meditates in lotus pose and turns her head all the way around after the cursor. Click her and her hair stands up like a plasma ball: "Excuse me?" Scroll away and she waves.
Max holds a kettlebell at his shoulder and extends that arm toward the cursor. Every click is another lateral raise, and he gets more tired each time: from "Easy." to "Ship it… tomorrow." He waves with his free hand.
Dan sits at a desk with an old adding machine. He looks left or right, turns the crank on click and says, "Forty-two." He waves while keeping his other hand on the crank.
Alan holds a pair of scales. The scales tilt while he watches the lower pan. On click he asks, "For the record… or am I recording you?" Then he waves.
I am an IT analyst, not a web designer or a front-end developer. I built the characters together with Claude Code.
The idea came from LEVÉ by filatov.design. I liked how the whole design was thought out, and I could not stop thinking about the cursor-following effect.
If you want to see the characters move before reading on, the videos are in the LinkedIn article, or just open one of the author pages above.
How does the cursor pick a frame?
Everything starts with one image of the character: the base frame (the four pictures above). Every video starts from it and returns to it. Because the first frame is also the last frame, the videos chain back into the resting pose without a visible cut.
The cursor video never actually plays on the page. It is a clip of the character turning its head, and the browser picks single frames from it depending on where the cursor is. Cursor on the left: a frame looking left. Cursor higher up: an upward glance.
The browser measures where the cursor is relative to the character's head. For Mona it uses the angle around the head; for Dan it only checks whether the cursor is to the left or right. It picks the frame whose direction fits best, and the head glides toward it instead of jumping.
A small amount of stickiness keeps the current frame from changing too easily. Without it, the head kept jumping between two distant frames, because it passes the same direction twice in the loop. The head also has a speed limit, and the next jump in the video waits until the previous one is finished, so a fast cursor does not make it flicker.
The browser needs a head map: a table that assigns a direction to every frame. For Mona and Dan, a script uses MediaPipe to find the face and mark points around the eyes and nose. From those points, the script works out where the head is turned.
For Max and Alan, MediaPipe is not used. Their scripts follow a colored prop instead: the teal kettlebell for Max and the green pan of the scales for Alan.
Frames where detection finds no face, a full profile for example, are dropped from the map. They stay in the video, but the browser never picks them because they are not candidates in the map.
To make the flipping smooth, the video is re-encoded so every frame is a keyframe. Each frame then holds the full picture and can be shown on its own. Claude Code wrote the ffmpeg script for that.
Why frames from a video and not images?
You can build the effect with separate images, one per frame, or with a sprite sheet where all frames are tiles in one large image.
I went with video because the LEVÉ demo inspired me and I liked its overall design. Video also gives the characters another ability: they can speak, with their lips in sync with the voice. And video turned out to be the smallest option: one MP4 was smaller than the same frames saved as separate images, and it is one file instead of hundreds.
What happens on a click, and why three clips?
A click starts a short clip. For a moment the character stops following the cursor, makes a move and says its line. When the clip ends, it is back on the base frame and follows the cursor again.
Each character has three click clips. Claude Code suggested that three clips would let the reaction build step by step, which brings in some humour and fun.
A click during a clip is ignored. The clips take turns in a fixed order.
The clips are generated in Kling with sound, so the mouth movement and facial expression match what the character says (lip sync comes from the video model itself). ElevenLabs Speech to Speech then swaps the original voice for the character's voice, keeping the timing of the line, so the mouth still matches the words.
Scrolling down drives the goodbye. The further you scroll, the further the hand moves. Scroll back up and the wave runs backwards until the character sits in its starting pose again.
There is no cursor on mobile, so the character turns its head on its own. A tap plays a clip with a line, and scrolling drives the goodbye the same way as on desktop. When the mouse stops for a moment, the head also starts turning on its own, on desktop too.
What went wrong in the browsers?
The trickiest problem was Firefox. The first time a video played with sound, Firefox froze the picture. So all videos now run muted and the sound plays separately, started by the click.
On iPhone, Safari does not load a video in advance. The head was therefore only a still picture. Starting the video for a split second and stopping it immediately fixed that.
I tested Firefox, Chrome and iPhone. Older browsers and weak phones are still untested.
How heavy is it to load?
Videos where every frame is a keyframe are bigger, but one character still adds up to only a few megabytes.
Only the cursor video loads with the page. The click clips start downloading when the cursor first enters the character, the goodbye when you first scroll.
How did I judge a clip?
A clip must not have deformed body parts and must not break physics. If Max's arm passes through his neck, that is wrong. If the white of an eye goes outside the edge of the eye, that is wrong too.
Models today are good and can make a character believable, but the prompt still has to be good. With a strict prompt, we made Alan stiff, so we had to loosen it and fix him.
A lot of this starts with the base frame. Three rules held up for the image from GPT Image: a white background, free space above the head, and no logos or text, because the video model warps them as soon as anything moves.
After generation, a script checked whether the background was really white, whether the loop closed without a jump and where the head was looking. Whether the clip was good, I decided.
The failures included:
- Mona's white background darkened as she flew away. A script fixed the background, but I eventually replaced the fly-away with a plain wave.
- Max's kettlebell arm crossed his neck twice. We replaced the complex movement around the head with a simple arm extension to the side. A five-second test became the final cursor video.
- Dan's eye-roll put the whites of his eyes outside the eyes. We removed the eye-roll from the prompt where it was not important.
- A sheet of paper fell "out of nowhere" at the end of Dan's clip. The model could not handle the prop and the required ending. The prompt needed to describe not only what the character does with an object, but also where the object ends up.
- Dan's head did not circle. It only turned left and right, so we kept that result and made him follow the cursor's horizontal position.
- Alan looked stiff in the first clips. Only his head moved, so we added breathing, shoulder movement and a shift of weight to the prompt.
Of 33 generated clips, 20 made it to the site: 1.65 attempts per usable clip on average. The failed clips are in the video in the LinkedIn article.
What did it cost, and how long did it take?
For all four characters, the 33 generated clips cost $20.79 in total. Recoloring the voices took 1,793 ElevenLabs credits.
| Character | Clips generated | Used on the site | Video cost (1) | Voice credits (2) |
|---|---|---|---|---|
| Mona | 10 | 5 | $6.51 | 617 |
| Dan | 7 | 5 | $4.41 | 420 |
| Max | 7 | 5 | $4.41 | 252 |
| Alan | 9 | 5 | $5.46 | 504 |
| Total | 33 | 20 | $20.79 | 1,793 |
(1) Kling via OpenRouter. The cost includes unused attempts.
(2) ElevenLabs.
That is not the full price of the project. GPT Image and Claude Code ran on subscriptions, and face detection and video editing ran locally, so they added no fees.
The video and voice connections were already set up from earlier projects: videos through OpenRouter, voices through ElevenLabs. Claude Code only had to be pointed at the right service.
Mona took the most time because we were still figuring out the effect, building the mechanism and wiring it into the site. The other characters reused that foundation. The time is estimated from activity in the Claude Code conversations. Dan and Max were built in parallel in two sessions, so the total real time was a little shorter.
| Stage | Approx. active work time |
|---|---|
| Demo analysis and Mona: mechanism, test demo and wiring into the site | 5.2 hours |
| Dan, including move design for the other three characters | 1.7 hours |
| Max | 1.4 hours |
| Alan | 1.3 hours |
| Total | 9.6 hours |
Claude Code processed 207.8 million tokens in total. Most of that was conversation context being reused, not newly generated content.
| Usage type | Tokens | Share | What it means |
|---|---|---|---|
| Cache read | 204.7 million | 98.5% | Reuse of context that was already processed |
| Model output | 0.78 million | 0.4% | Newly generated content, such as answers, code and prompts |
| Other input tokens | 2.3 million | 1.1% | The rest of input processing, including cache creation |
On every step of a task (read a file, write code, run a script, check, fix), the model gets the whole context so far again. The cache makes that reuse cheaper, but a longer conversation still means more of it. That is why I opened a separate session for each character and passed only the important information between them, in summary notes (how to keep that kind of context in files). For the later characters, cache reads were roughly five to six times lower than for Mona, because they reused the mechanism instead of building it.
Claude Code runs on a subscription, so individual tokens are not billed. I still track usage to understand how many tokens, and what cost, similar work would take if it were scaled up and automated through the API.
Who did what?
Claude Code explained the principle, prepared prompts, suggested some of the moves and lines, wrote scripts, built the shared code base and handled browser-specific fixes. It also kept the documentation, the cost log and the handoff notes.
I designed the character concepts, moves and lines, judged what movements the model could handle, simplified prompts when needed, judged every clip and tested the result in Firefox, Chrome and on mobile.
It sometimes moves me how capable AI already is. Hints and descriptions are enough; it fills in a lot on its own and flags things before I would have thought of them.
One tip: don't leave a conversation without checking what is worth saving. Claude Code often keeps scripts and data only in its scratchpad, and they are gone when the session closes. If you tuned them through many iterations, that hurts. And when the context is overflowing, try: "write me a handoff to continue in the next conversation".
What would I do differently?
With more time, I might think through the characters' poses more carefully, perhaps having them stand.
To make the characters roughly the same size in a wide video, only Mona is shown whole. With the others, the video sometimes cuts their hands off out of frame. A professional video maker would notice that and be bothered by it.
I also used only Kling for the characters. I knew Seedance 2.5 from another project: through OpenRouter it refuses photorealistic faces as input, even AI-generated ones. Its official route needs your own BytePlus account, with the character registered in their portrait library and a signed authorization. Kling 3.0 takes faces through OpenRouter too (as of October 2026).
Did it help?
I can't tell that so quickly. I can come back to it later and look at the visit numbers in Google Search Console.
It is also about something else. This change can lead to more articles, and its playfulness has a chance to spread further than very technical articles.
It's a small thing, but it is also a piece of design knowledge I picked up together with Claude Code. AI is an incredibly well-read partner for learning: it knows many principles, or looks them up on the way to the result, and guides you there.







