1
1234567890
1234567890
%

Ove

A glass mascot for OverSub that reacts to what happens in the app and stays light next to a running game.

Type

Personal project

Product

OverSub, a Mac app that translates game dialogue and reads it aloud

Role

Design and build: character, motion and in-app behavior

Year

2026

Platform

macOS, Apple silicon

Tools

three.js, Swift, Core Animation

Status

Shipped in OverSub 1.1.86, October 2026

Expressions

Eight faces for the quiet moments.

When nothing is happening, Ove picks one of these every 5 to 9 seconds. Hover or tap a face to play it.

Context

A friendly face for a technical app.

OverSub is a free Mac app I design and build on my own. It reads dialogue in games, translates it and reads it aloud. The main window has three round controls: Subtitles, Voice-over in the middle, and Screen translation.

Underneath, it is a technical tool: screen recording permission, text regions, translation services. To many players that sounds like a heavy setup they would rather avoid. I wanted a character with a soul to make it feel approachable: a face for the voice, a way to show what the app is doing without words while your eyes are on the game, and a personality people remember.

Until October 2026 the middle control was a flat letter-O mascot, the same shape as the app icon. Ove replaced it: a glass head with an orange core that blinks, looks around, reads lines aloud and reacts to what the app is doing.

My role

One person, the whole character.

OverSub is a one-person project, and I design and build it end to end. For Ove I designed the character and its eight expressions, built the 3D head in three.js, animated every state, and wrote how it behaves in the app: what it reacts to, how often, and what it never does.

Constraints

Constraint

What it meant

It runs next to a game

Any animation that redraws the window every frame costs CPU the game needs.

It is also a button

Clicking Ove turns the voice on and off, so it has to stay readable as a control.

128 points wide

Ove sits between two 100-point controls in the main window. Every face has to read at that size.

Light and dark

The main window follows the system appearance.

The player is busy

Reactions must not cover the line being read or pull attention from the game.

Process

From a flat O to a glass head in a week.

The first mascot shipped on 7 October 2026 in version 1.1.43. On 10 October I built the glass head as a 3D prototype in three.js, with a spring-based motion engine and full mouse play: it follows the cursor, sulks when you leave and gets dizzy when you spin it. The app version went through eight builds that day, from 1.1.75 to 1.1.82, and shipped publicly in 1.1.86 on 13 October. Ove also narrates OverSub’s launch video.

  1. 7 Oct · 1.1.43Flat letter-O mascot
  2. 10 OctGlass head prototype in three.js
  3. 10 Oct · 1.1.75 to 1.1.82Eight app builds
  4. 13 Oct · 1.1.86Public release
  5. 13 OctNarrates the launch video

Decision

Ove reacts to real events, not to a timer.

Every expression outside idle time is tied to something the app actually did. The first translated line of a session makes Ove happy. A paused translation service makes it sad, a key out of quota makes it angry, a stuck text recognizer makes it dizzy, and nothing translating at all makes it cry.

If Ove is busy reading, a reaction waits up to 8 seconds, the more serious one goes first, and the same reaction plays at most once every 20 seconds.

Decision

Lively while you wait, quiet while you play.

Before a session starts, Ove does something small every 5 to 9 seconds, and every third time it plays a short scene with the controls around it: it eyes the Start button and winks, dozes off and wakes up embarrassed.

When subtitles are coming in, the player is in the game, so Ove only looks around every 30 to 60 seconds. Hovering over Ove tickles it. Resting the pointer on a control for 0.3 seconds makes Ove turn to look at it; passing over it does nothing.

Looking around, every 30 to 60 s while you play
A scene while you wait: dozing off

Decision

Render once in 3D, play back as frames.

A few days before Ove, I measured what per-frame drawing costs in this window: any animation redrawn by SwiftUI on every frame made the whole glass window redraw, at 14 to 16% of one CPU core. So Ove is never rendered in 3D inside the app. Every state is rendered ahead of time into frame sheets, and Core Animation flips through them.

Each frame is rendered twice, on black and on white, to recover the real transparency of the glass, so one set works in light and dark mode.

frame 1 / 72
0.3%of one core while reading
0.4%idle
0%window covered

The reading loop: 72 frames at 24 fps, 280 pixels each. Measured on an M1 Pro.

Decision

Never two faces on screen.

A cross-fade between two poses shows two pairs of eyes for a moment, and that single frame is enough to break the character. Every clip starts and ends on Ove’s resting face. If a clip is interrupted, it rewinds to that face before the next one starts, and a turned head comes back to center first. I checked each build frame by frame from screen recordings: version 1.1.78 went from 15 broken frames to none.

Some ideas stayed in the prototype. Following the cursor, petting and teasing Ove with the mouse felt alive in a browser, but next to a game they read as noise, so the app keeps only the tickle and the glance at a control.

Illustration: cross-fade between two poses
Rewind to the resting face first

In the app

Where Ove lives.

Ove in the main window, reading a line, and in the dialogs where it stands in for the app icon.

Dark mode, reading a line. The speech bubble points up at Ove.

Light mode, reading the voice preview.

The update dialog. Ove stands in for the app icon.

The support window. The bigger the amount, the happier Ove gets. No amount makes it sad.

Launch video

Ove narrates OverSub’s 30-second launch video.

Voice and captions are in Vietnamese.

Outcome

Shipped, and light enough to forget.

Ove shipped in OverSub 1.1.86, a public release on GitHub, on 13 October 2026. It appears in the main window, the update dialog, the bug report dialog and the support window.

8

Idle expressions

Played while nothing is happening.

5

Event reactions

Happy, sad, angry, dizzy and cry.

1,567

Frames at 24 fps

Across 27 frame sheets.

0.3%

CPU while reading

Of one core, about 0.4% idle.

Counted from the frame sheets in OverSub 1.1.86. CPU measured on an M1 Pro with the window visible.

What I learned

A companion has to feel alive.

I had never made a 3D mascot before. The flat mascot could show what the app was doing, but it could not feel alive, or like it was keeping you company. That is why I moved to 3D and spent most of the work on emotion and the small signs of life: a blink, a glance at the Start button, a sneeze, dozing off between lines.

Thanks for reading

If you are building a product that needs a system, a flow or a character, I’d like to hear about it.

  • Astræus Design System • Astræus Design System •
  • Astræus Design System • Astræus Design System •
  • Astræus Design System • Astræus Design System •
  • Astræus Design System • Astræus Design System •
  • Astræus Design System • Astræus Design System •
  • Astræus Design System • Astræus Design System •
  • Astræus Design System • Astræus Design System •
  • Astræus Design System • Astræus Design System •
  • Astræus Design System • Astræus Design System •
  • Astræus Design System • Astræus Design System •
  • Astræus Design System • Astræus Design System •
  • Astræus Design System • Astræus Design System •

A design system I built solo, where light and dark share one elevation rule and brands swap as token sets.