• The Portfolio of Paul Butterworth - virba_Blue9_Banner

Virba

I've always loved elegant design — in films, tools, games. At some point, I started wondering: why shouldn't a word game feel as considered as the objects I cherish?

I'd been playing word games for years — the usual suspects — and what bothered me wasn't the puzzles but everything around them. The ads. The aggressive upsells. The feeling that the game was designed to extract rather than delight.

I wanted something different. A word game that appeared like a beautiful object. Something you'd be glad to have on your phone. Something that respected your time and your intelligence.

So I built one.


The Numbers Behind the Words

Virba launched with 36,867 hand-curated puzzles — enough for over 100 years of daily challenges.

But quantity without quality is just noise. Each puzzle went through an algorithmic quality gate. The generator evaluates every 6–8 letter word in English as a potential seed, tests all possible words that can be formed using a Trie data structure for O(n) lookup, then scores the results:

Quality Score =
    min(wordCount, 60) × 1.0
  + max(0, wordCount - 60) × 0.3      // diminishing returns after 60
  + (5+ letter words 5) × 10
  + (6+ letter words 3) × 15
  + (7+ letter words 1) × 20
  + premiumWordCount × 8              // crossword-worthy finds
  + (uniqueLetters 7) × 5
  + (vowels = 2–3) × 5                // balance bonus
  + uncommonLetters × 5               // Q, Z, X, J

A puzzle needs to clear a Quality Score of 75 to ship. Of 127,000+ candidates evaluated, 36,867 made the cut — a 29% acceptance rate.

The word-finding algorithm itself uses recursive backtracking through the Trie:

func findAllWords(from letters: [Character], node: TrieNode, path: String) {
    if node.isEndOfWord && path.count >= 2 {
        results.insert(path)
    }
    
    for (index, char) in letters.enumerated() {
        if let nextNode = node.children[char] {
            var remaining = letters
            remaining.remove(at: index)
            findAllWords(from: remaining, node: nextNode, path: path + String(char))
        }
    }
}

Try each available letter, descend the Trie, remove the used letter, repeat. The Trie structure means we abandon paths early if no words exist down that branch. For a 7-letter puzzle, this evaluates thousands of combinations in milliseconds.


Finding the Grid

The first visual challenge was deceptively simple: how do you arrange letters?

Word games live and die by their letter layouts. Too orderly and it feels mechanical. Too chaotic and the eye can't parse it. I needed something that felt organic but remained legible — a pattern that invited exploration.

I tried grids. Honeycombs. Spirals. Freeform scatters. Then polyominoes — the interlocking shapes behind Tetris.

The cascading stair pattern emerged from there. Letters tumbling down the screen like thoughts forming. Your eye follows the flow naturally. Words reveal themselves as you trace the connections.

It sounds small. It took three weeks.


The Colour of Words (24 of Them)

Early Virba was functional. It worked. It was also, frankly, forgettable.

I kept thinking about the books on my shelf — the ones I'd kept not just for their contents but for their spines. Deep teals, burnt oranges, embossed gold. Books designed to be objects, not just vessels. What if a word game could feel like that?

Virba now features 24 distinct colour themes, one for each hour of the day. Each is named for a mood or material, and loosely paired with a literary work in my head — though the game never says so explicitly.

Midnight brings Obsidian — silver text on near-black. Noon is Coral, warm terracotta with navy accents. Late evening delivers Inferno, deep crimson and gold.

Each theme isn't just a coat of paint. The tile colours shift. The backgrounds breathe differently. Even the sound frequencies adjust subtly to match the mood.

The goal: whichever hour you play, Virba feels like it dressed up for you specifically.


Making Tiles Feel Alive

A letter tile is just a square with a character on it. In theory.

In practice, tiles are the soul of the game. You're staring at them constantly. They need to feel good — not just look good, but feel tactile, responsive, satisfying.

This is where Metal comes into play.

Virba's tiles are rendered entirely on the GPU using custom Metal shaders. Each tile exists in a subtle 3D space with real lighting calculations — the same physics used in film visual effects, scaled down to a word game:

// Diffuse (Lambert)
float NdotL = max(dot(normal, -lightDirection), 0.0);
float3 diffuse = baseColor * NdotL * lightColor * intensity;

// Specular (Blinn-Phong)
float3 halfVector = normalize(-lightDirection + viewDirection);
float NdotH = max(dot(normal, halfVector), 0.0);
float shininess = (1.0 - roughness) * 128.0;
float3 specular = pow(NdotH, shininess) * lightColor * 0.5;

// Fresnel edge glow
float fresnel = pow(1.0 - max(dot(normal, viewDirection), 0.0), 3.0) * 0.12;

// Final colour
litColor = ambient + diffuse + specular + fresnel;

The Fresnel term is what gives tiles that subtle edge glow — brighter at grazing angles, invisible head-on. It's the same physics that makes water reflect more at shallow angles.

When you tap a tile, it doesn't just highlight. It flattens — pressing down into the screen, losing its 3D depth entirely. The shadow beneath disappears. The tile is committed, locked in. It's the difference between hovering over a piano key and pressing it.

// Selected tiles flatten to 2D — no depth, no shadow
if (instance.isFlat != 0) {
    pos.z = 0.0;
}

Unselected tiles sit in 3D space, casting soft Gaussian shadows:

float dist = length(texCoord - 0.5);
float shadow = exp(-dist * dist * 18.0);  // Gaussian curve

The 18.0 controls spread. This value was tuned by eye until it felt right.

Submit a word, and the tiles come alive — shuffling, rotating, settling into new positions through 3D space. The depth returns. New letters arrive. The cycle begins again.

These micro-interactions take milliseconds. You barely notice them consciously. But remove them, and the game feels dead.


Sound Without Samples

Here's something unusual: Virba contains no audio files.

Every sound you hear — every tap, every word confirmation, every triumphant chord when you find a golden word — is synthesised in real-time using AVAudioEngine.

Why? Control. A sampled sound is fixed. A synthesised sound can respond to context:

  • Combo sounds pitch upward as your streak builds
  • Golden word chords are richer than bronze
  • The game-over arpeggio descends gracefully
  • Time bonuses shimmer with harmonics

The sound system generates sine waves, applies amplitude envelopes, mixes waveforms (celesta for clarity, marimba for warmth, shimmer for reward), and outputs the result — all computed on the fly. Common sounds are pre-cached at launch so there's never a hitch during gameplay.

It's almost certainly overkill for a word game. But "almost certainly overkill" is another way of saying "worth doing right."


The Dictionary Problem

Word validation sounds simple: is this a word? Yes or no.

It's not simple.

English contains roughly 170,000 words depending on who's counting. Bundling them all would bloat the app. Trim too aggressively and players find valid words rejected.

Rejecting a valid word breaks trust instantly. The player knows they're right. If the game disagrees, the game is wrong — and the player won't forget it.

Virba uses a hybrid validation system:

  1. Primary: Trie dictionary — 80,272 curated words loaded into a Trie structure for instant lookup. Every word verified as legitimate English.
  2. Fallback: UITextChecker — Apple's built-in spell checker catches valid words not in our dictionary.

If a player finds a word we didn't anticipate, UITextChecker validates it and the game rewards a +5 second time bonus. We call these "bonus words" — the system's way of saying "nice find, we didn't think of that one."

This means Virba's vocabulary effectively includes every word Apple's dictionary recognises, while keeping the core app lean.

We also include all 124 official Scrabble two-letter words (Collins Scrabble Words) — because nothing should stop you playing qi or xu.


The Fairness Problem

This one kept me up at night.

Virba has tiers — a progression system that rewards how well you play each puzzle. Early versions used fixed point thresholds:

if points >= 40000: tier = Laureate
if points >= 25000: tier = Author
if points >= 12000: tier = Poet
...

Simple. Except it wasn't fair.

Some puzzles are generous — common letters, obvious words, high-scoring opportunities everywhere. Others are stingy — awkward consonants, obscure vocabulary, points hard-won.

Under fixed thresholds, a generous puzzle was a free pass to Laureate. A stingy one punished you even when you played brilliantly.

The fix: percentage-based tiers. Your tier now reflects what percentage of a puzzle's maximum possible score you captured:

percentage = (playerScore / puzzleMaxScore) × 100

tier = match percentage {
    < 10%   Scribe
    < 20%   Wordsmith
    < 35%   Poet
    < 50%   Author
    < 65%   Laureate
    < 85%   ?
    < 95%   ?
     95%   ?
}
Tier Percentage Description
Scribe 0–10% Recording your journey
Wordsmith 10–20% Crafting with care
Poet 20–35% Finding your rhythm
Author 35–50% Commanding language
Laureate 50–65% Crowned in words
Secret Tier 65–85% Hidden until earned
Secret Tier 85–95% Hidden until earned
Secret Tier 95%+ Hidden until earned

The thresholds were chosen to feel achievable but meaningful. Reaching Author (50%) should feel like genuine mastery. Reaching Laureate (65%) should feel rare. There are three tiers beyond that are hidden in the UI until unlocked. Mystery as reward.

The tiers serve two purposes: tracking your progress over time, and bragging rights. Watching your average climb from Wordsmith to Poet to Author tells a story. The badges mark the journey.

Each tier icon was hand-drawn in ink, designed to evoke Victorian wood engravings. Scroll, quill, harp, book, laurel — the imagery deliberately classical, deliberately earned. No gradient-filled achievement badges here. The aesthetic had to feel like something worth framing.

A puzzle with maxScore = 8,000 and a puzzle with maxScore = 24,000 now have equivalent paths to each tier. The maths enforces fairness that fixed thresholds couldn't.


Theme Words: The Hidden Layer

Some puzzles have a theme. Not all — discovering one feels special.

When a puzzle's valid words overlap with a theme category, the game notices. Find three theme words and bonus tiles appear — two extra letters to extend your run.

The theme system contains 16 categories across three rarities:

Rarity Frequency Examples
Common 60% Creatures, Nature, Food, Time, Emotions
Uncommon 30% French, Latin, Greek, Japanese, Architecture
Rare 10% Shakespeare, Poetry, Mythology, Palindromes

Rare themes make certain days special. Finding qi, qat, and qoph in a Q Words puzzle? That's a good day.

Each category contains hand-curated trigger words — not just obvious ones, but crossword-solver favourites. The Creatures list includes erne (a sea eagle), stoat, egret. The French list knows soufflé, avant-garde, cachet. Poetry includes iamb, caesura, enjambment.

These lists were a labour of love. They're also what separates Virba from games that feel algorithmically hollow.


The Golden Ratio

All spacing in Virba follows the Fibonacci sequence: 8, 13, 21, 34, 55, 89.

This isn't mysticism — it's constraint. The value isn't that Fibonacci ratios are inherently magical. It's that every element shares the same proportional logic. When spacing, timing, and layout all derive from one system, nothing fights for attention. The interface becomes quiet.

The same principle extends to timing. Animation durations, combo windows, the pulse of the border timer — all derived from the same ratios.

You won't notice this consciously. That's the point.


What I Learned

Building Virba taught me something I should have known from three decades in visual effects: craft is in the details people don't consciously see.

A film audience doesn't notice good compositing. They notice bad compositing. The same is true for apps. Nobody opens Virba and thinks "what a satisfying tile animation" or "this shader lighting is excellent." They just enjoy playing.

The work vanishes into the experience. That's the goal. That's always the goal.


The Technical Spec

Attribute Detail
Framework SwiftUI + Metal
Architecture MVVM
Minimum iOS 17.0
Puzzle Library 36,867 puzzles (39MB compressed)
Dictionary 80,272 words + UITextChecker fallback
Colour Themes 24 (hourly rotation)
Tier System 8 tiers (5 visible, 3 secret)
Audio Procedural synthesis via AVAudioEngine
Rendering Custom Metal shaders, 120fps capable
Privacy Zero tracking, zero analytics
Price One-time purchase (A$4.99)


What's Next

Virba continues to evolve. Updates refine, polish, expand. Meanwhile, the next game is already taking shape. It involves numbers. It involves speed. It's called Nimbr. More on that soon.