Barbershop tags in numeric notation
numtags is a mobile-first web application designed specifically for barbershop singers to quickly find, learn, and share barbershop tags. Tags are short, harmonically rich pieces of music meant for spontaneous group singing. The app uses a custom numeric notation system that makes it easy to read, learn, and teach tags on any device — and it can convert a tag from MusicXML, MIDI, or a photo/GIF of sheet music into that notation.
FABLE_SPEC.md is the full product/engineering spec.
- Search: Search by ID, title, lyrics, or arranger
- Import & convert: MusicXML (
.xml/.mxl) and MIDI files convert entirely in the browser; photos/GIFs/PDFs convert through a self-hosted OMR service (seeservices/). Every conversion lands in a review screen — nothing is saved unseen - Write your own: type a tag in plain-ASCII shorthand with a live preview
- Your library: imports and drafts live in a private on-device library (IndexedDB)
- Two layouts: wrapped staff systems (like printed music) or one continuous scrolling line — toggle per tag, default in Settings
- Mobile First: Optimized for mobile devices with PWA capabilities
- Share as Image: Generate images of tags to share
- Offline Ready: browsing, writing, and MusicXML/MIDI import all work offline
- Open Source: Openly licensed and welcoming community contributions
- Dark Mode: Because let's face it, we mostly sing tags at afterglows
numtags uses a movable-Do numeric system where numbers represent scale degrees:
1 = Root (Do)
2 = Second (Re)
3 = Third (Mi)
4 = Fourth (Fa)
5 = Fifth (So)
6 = Sixth (La)
7 = Seventh (Ti)
Each line represents a voice part (Tenor, Lead, Baritone, Bass). The notation is stored as plain ASCII — what you type is what's stored — and the app draws the pretty marks (♯/♭, octave dots, tie arcs) when displaying it:
| You type | Meaning |
|---|---|
#2 / b3 |
sharp / flat |
5' / 5, |
octave up / down (stack: 5'', 5,,) |
3/ / 3// |
eighth / sixteenth note |
3. |
dotted |
~4 |
tie (continue the previous note) |
- |
hold one more beat |
0 |
rest |
X |
posted — hold until the cut |
| |
measure boundary |
_ |
(lyric line) beat with no new syllable |
Tenor: 3 - 3 - | 4 ~4 3 - |
Lead: 1 - 1 - | 1 ~7, 1 - |
Baritone: 5, - b7, - | 6, ~5, 5, - |
Bass: 1, - 5, - | 4, ~2, 1, - |
"My town, my town."
The full guide lives in the app under Guide (/notation).
- Node.js 20+
- npm
- Clone the repository:
git clone https://github.com/bumbleblue/numtags.git
cd numtags- Install dependencies:
npm install- Start the development server:
npm run dev- Open your browser and navigate to
http://localhost:5173
npm test # vitest — parser, normalizer, encoder, golden tags
npm run check # svelte-checknpm run buildBuilds with the Cloudflare adapter. The markdown catalog compiles to a bundled snapshot (npm run generate-tags) before every dev/build.
services/ contains a FastAPI scaffold for the two network features: image→MusicXML OMR (homr) and the Git-backed collaborative catalog (a bot commits edits with the editor's name; history + revert). The app runs fully without it — image import and Publish simply show as unavailable until PUBLIC_SERVICE_URL is configured. See services/README.md.
numtags/
├── src/
│ ├── lib/
│ │ ├── notation/ # ASCII grammar: parse, normalize, transform (+tests)
│ │ ├── score/ # MusicXML/MIDI → ScoreModel → encode (+tests)
│ │ ├── library/ # IndexedDB private library
│ │ ├── components/
│ │ │ └── notation/ # NotationRenderer + BeatCell (CSS-drawn marks)
│ │ ├── data.ts # catalog ∪ local library search
│ │ └── types.ts # Tag types
│ ├── routes/ # Library, tag pages, import, review, guide, settings
│ ├── app.css
│ └── app.html
├── services/ # OMR + catalog bot service (FastAPI, not deployed)
├── static/ # PWA assets
├── data/tags/ # The catalog (Markdown + YAML, canonical ASCII)
└── package.json
Tags are stored as Markdown files with YAML frontmatter (canonical ASCII notation):
---
title: "So Tired of Waiting For You"
tag_id: 53
arranger: "Renee Craig"
difficulty: "Medium"
source_url: "https://www.barbershoptags.com/tag-53-So-Tired-of-Waiting-for-You"
date_added: "2008-12-15"
parts: 4
lyrics: "So tired of waiting for you."
original_key: "G"
origin: "catalog"
---
| 1 3 5 | 4 6 - 1' | 1' - |
| 1 1 #1 | 2 b3 - 4 | 3 - |
| 1 b7, 6, | 1 1 - b6, | 5, - |
| 1 5, 3, | 6, 4, - 2, | 1, - |
So tired of wait-ing _ for you.Formatting notes:
- Each staff is 4 voice lines followed by its lyric line(s); blank lines separate staffs
- In lyric lines, a space advances one beat,
_holds, andslee-pysplits a word across two beats - Beats before the first
|are a pickup - The 5 original hand-transcribed tags double as the golden test set — changes to them must be deliberate (tests will tell you)
We welcome contributions! Here's how you can help:
The easiest way is in the app: Import → Write it yourself, then save and export/share. To contribute to the shared catalog today:
- Create a new Markdown file in the
data/tags/directory (format above) - Submit a pull request
(In-app publishing to the catalog exists behind the catalog service and goes live when that service is deployed.)
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Commit your changes:
git commit -m 'Add amazing feature' - Push to the branch:
git push origin feature/amazing-feature - Open a Pull Request
- Follow the existing code style (Svelte 5 runes for new components)
- Add TypeScript types and tests for new features
npm testmust stay green — the golden-tag tests guard the notation core- Update documentation as needed
numtags is a Progressive Web App with the following features:
- Offline Support: app shell + catalog cached; MusicXML/MIDI import and authoring work offline
- Installable: Add to home screen on mobile devices
- Fast Loading: catalog ships as a build-time snapshot
- Responsive: Works on all device sizes
- Framework: SvelteKit (Svelte 5)
- Styling: TailwindCSS (Base16 Default Dark palette, role-named color tokens)
- Search: Fuse.js
- Imports: DOMParser (MusicXML), @tonejs/midi, fflate (.mxl), homr (OMR, self-hosted)
- Storage: IndexedDB (idb) + Git-backed catalog
- PWA: Service Worker + Manifest
- Deployment: Cloudflare (adapter-cloudflare)
This project is licensed under the MIT License - see the LICENSE file for details.
By contributing to numtags, you agree to the following terms:
- Source Attribution: All tags are sourced from barbershoptags.com and are only rewritten in our numeric notation system
- Original Links: We maintain links to the original publication location on barbershoptags.com
- CC0 License: Any copyright generated through tag translations or contributions to this project shall be licensed under CC0 1.0 Universal (Public Domain)
- No Original Composition: We do not create original musical content - only translate existing tags into numeric notation
- MIT License: The website code is licensed under the MIT License
- Contributions: By contributing code, you agree to license your contributions under the MIT License
When you contribute tag translations or other content to numtags, you:
- Confirm that you are translating from the original source on barbershoptags.com
- Agree to license any copyright in your contributions under CC0 1.0 Universal
- Understand that we maintain attribution to the original source
- Accept that your contributions will be freely available to the public domain
This ensures that all tag translations remain freely available and that we respect the original sources while making barbershop tags more accessible through numeric notation.
- GitHub Issues: Report bugs or request features
- GitHub Discussions: Join the conversation
Made with ❤️ for all of you barbershop-obsessed people!