wanessalabs standalone production July 28, 2026

Kalimba Easy

Every kalimba tab site is an ad farm with no notion of difficulty. This one rates every song 1 to 5 and shows exactly what made it that hard.

ReactTypeScriptViteTailwindZodVitestWeb Audio APICapacitorCloudflare Pages
Sorted by how hard a song actually is on your kalimba, not by how well known it is.
Sorted by how hard a song actually is on your kalimba, not by how well known it is.
Filter by instrument, level, length and mood before committing to learn something.
Filter by instrument, level, length and mood before committing to learn something.
Every song states its level, its tine range, and whether the transcription is verified.
Every song states its level, its tine range, and whether the transcription is verified.

Search for kalimba tabs and you get an ad-choked SEO farm with an unsorted pile of songs and no notion of which are hard. A nursery rhyme and a two-octave chromatic pop arrangement sit in the same list, formatted identically. A beginner who bought a 17-key kalimba three weeks ago wants to know one thing: what can I actually play tonight.

The Rating Is the Product

The catalog is the demo. The thing being built is a difficulty engine that can say:

Level 1 of 5. No leap larger than a third, fits inside 5 tines, unhurried at tempo, 30 notes, one repeated phrase.

That sentence is generated, not written. Nine factors are measured, each emits its weighted contribution, and the explainer renders the two or three that actually drove the score. The engine is a pure function with no clock, no randomness and no I/O, so the level computed at build time and the level computed in the browser cannot disagree.

The Instrument Has Opinions

A C-major kalimba puts its longest, lowest tine in the centre and alternates ascending pitches outward, left then right. Two consequences fall out of that, and they reshaped the whole model.

Each side is a chain of thirds. A scale step is one tine away on the opposite side, so stepwise melodies alternate thumbs and feel easy. A third is one tine away on the same side. Which means a triad is three consecutive tines under one thumb and rolls easily, the opposite of piano intuition. The original difficulty rubric had a rule penalising chords; measuring the physics showed it was wrong, and the rule became “penalise a gap in one thumb’s run” instead.

The layout fans outward, so reach depends on register. C4 to G4 hugs the centre across 4 tines. The same five-note shape an octave up straddles both outer edges across 10. That correction split one factor into two.

There are also melodies the instrument simply cannot play. Seven notes, no sharps or flats, so a piece only works if some transposition puts every note on a real tine. Für Elise alternates E and D sharp and there is no D sharp. The app says so on a dedicated page rather than quietly altering the tune and calling it the piece, which is what the tab farms do.

A kalimba tab of a copyrighted song is a derivative of the composition. So every melody shipped is public domain, and every one carries a provenance block: composition title, composer, year, the legal basis, and a source URL you can check.

This is enforced by a build script that breaks the build, with no override flag. Flip verified to false on any song and the build stops with no catalog written. The Song schema is Zod strict with no lyrics field, so lyric text cannot ship by accident even for public-domain songs, because many carry copyrighted lyric translations.

Doing the research properly corrected five claims that had felt obviously true:

The pattern is worth naming: the famous publication and the familiar tune are routinely different events, decades apart. A date that looks like provenance often is not.

Note Names, Not Tine Numbers

Song files store C4 C4 G4 G4 A4 A4 G4, not 8 8 10 10 5 5 10. The build derives tine indices at compile time. Two reasons, and the second matters more.

A tine index is only meaningful against one layout, so if the physical tine order turns out to be wrong, note names re-derive and indices would all be garbage. And a human can check C4 C4 G4 G4 by humming it. Nobody can check a row of tine numbers, and a review gate nobody can perform is decoration.

Honest About What Is Not Verified

Thirty melodies shipped. Six are transcription-confident. The other twenty-four carry a flag, and that flag appears on the browse card in plain sight, not hidden behind a tap. The physical tine layout itself has not yet been confirmed against a real instrument, and that is recorded in the code, the roadmap and the app.

An app whose entire pitch is that it tells you the truth about difficulty does not get to be coy about its own uncertainty.

Bring Your Own Tab

The catalog can only ever contain what is legally free, which is the wrong shape for the songs people actually want. So the app accepts pasted tabs. Any kalimba number tab gets the same rating, the same generated explanation, the same best-fit transposition and the same player, and it never leaves the device.

The parser is permissive on purpose. It reads what it can and reports what it could not, token by token, rather than rejecting a whole paste over one stray character. Somebody copying from a video description should not be punished for a typo.

The Build

Vite, React, TypeScript and Tailwind on Cloudflare Pages, with Capacitor for a native shell. Zod validates the catalog at build time and is then deliberately kept out of the browser bundle, because the client validates nothing at runtime. That alone saved 67 kB.

React Router was removed. Every published version carries a high-severity advisory with no clean gap between them, and the standing rule is that security failures block deploys and get fixed at the source rather than worked around. The app used four of its APIs, so it got about ninety lines of routing with a pure, tested path matcher instead. npm audit now reports zero vulnerabilities.

Audio is synthesised rather than sampled: five partials with per-partial decay through the Web Audio API. No sample licence to clear, no asset weight, and the code says plainly that it is a plausible pluck rather than a measured model of any real instrument.

The app makes zero third-party requests. No ads, no accounts, no analytics, no backend, no fonts CDN. The content security policy has an empty allowlist because there is nothing to allow. Fonts are self-hosted as woff2. The full catalog is bundled, so it works offline after first load, and service worker registration is skipped on native where the assets are already local.

130 tests, every difficulty factor isolated by its own synthetic fixture, and a suite that pins the rendered explanation sentences character for character. That last one exists because two genuine copy defects survived a fully green test run and were only caught by reading real output.

Development timeline

12 logged updates over 3 days, 26 Jul 2026 to 28 Jul 2026.

  1. Phase 5, bring your own tabfeature

    The human asked for 30 more songs and for the truncated entries to be extended to full length. Both requests ran into the constraint already on the record: I cannot source real notation, and the truncated songs are truncated precisely…

  2. Tine labels on the diagramfeature

    The request was to invert the graphic. The user plays with their thumbs and said the picture reads upside down.

  3. Phase 2, core enginefeature

    Pure TypeScript engine, zero React, zero UI dependencies.

  4. Phase 3 complete, 30 songsfeature

    The human chose "push to 30 now, flags and all" over sourcing transcriptions properly or stopping at 13. Seventeen more encoded from memory with honest flags.

  5. Phase 3, batch 3 and a scaling problemfeature

    Target raised to 30 songs. Batch 3 delivered 3, not 5, and the reason matters more than the count.

  6. Phase 3, catalog batch 1feature

    Five songs encoded, the build gate written and proven, the source format changed. 5 of a planned 30 melodies.

  7. Phase 3, catalog batch 2feature

    Three more of my own Phase 0 bases were wrong. Every one was caught by verifying rather than recalling, which is now three batches running at roughly one bad claim per two entries.

  8. Phase 4a, foundation and Browsefeature

    Phase 4 split in two, because presenting the whole UI as one diff would have been roughly two thousand unreviewable lines. 4a is the app shell plus Browse, which is the screen that IS the product.

Show the earlier 4 entries
  1. Phase 4b, detail, player and ladderfeature

    Song detail, SVG kalimba, player and practice ladder. Also closed the security blocker carried out of 4a.

  2. Phase 6, ship configurationfeature

    Human decided the domain and asked to build through to a publishable state.

  3. Phase 0, auditdocs

    New project proposed from a diff-first, phase-gated brief. Phase 0 was read-only by instruction.

  4. Phase 1, POW docsdocs

    Phase 1 proceeded on the Phase 0 recommendations, which were not overruled. The nine open questions were resolved to their recommended defaults and the assumptions were stated before writing.

Written from this project's decision log as work happened, not afterwards. 1 entry was withheld from publication. See every project →

What's next

5 open items

Plans, not promises. Taken from this project's own roadmap, last updated 28 Jul 2026.