{"slug": "part-1", "title": "Part 1", "summary": "A developer built a web-based tablature editor for the Guitalele, an instrument with limited resources, using AI assistance. The project evolved from a simple text parser into a full-featured editor supporting multiple scores, metadata, and a custom shorthand notation. The developer faced challenges with the notation's usability and added polyphonic lines for melody and bass.", "body_md": "When I started this project around 3 weeks ago, I never thought this one project would compel me to face so many challenges.\n\nIt started with a simple idea. I needed something to practice the Guitalele, an instrument with a very limited fan following that never reached the heights of the Guitar or Ukulele. Because of that, resources like tabs, scores, and specialized tuners are incredibly scarce.\n\nSo, I sat down to build a simple text area that would take my own musical shorthand and parse it specifically to Guitalele standards.\n\nUsing AI as my primary co-pilot, we started building. And then, the engineering snowball completely took off.\n\nThe first version was laughably simple — a `<textarea>`\n\nand a `Parse`\n\nbutton. You paste some shorthand, hit the button, and hope something renders on the other side.\n\nFor about an hour, that was enough.\n\nThen the cracks showed. I needed to manage multiple scores, not just one. I needed to edit old ones, not just create new ones. I needed parameters like time signature, instrument, capo position, and a description for each score. A raw textarea was not going to cut it.\n\nSo the editor grew. A dropdown to switch between existing scores — all stored locally for now. Input fields for metadata — name, time signature (4/4, 3/4, 6/8, 2/4, 2/2), instrument (Guitalele for now, but the door is open), capo position (0 to 12). A publish/draft toggle. A delete button with a confirmation dialog. Auto-resizing textarea that grows as you type. A manual modal referencing every shorthand rule I had invented so far. No cloud, no server — just React state and a dream.\n\nOnce the basic editor was ready, the next itch started. There is a classic dialogue in the movie 'Yeh Jawaani Hai Deewani' -\n\nYaadein mithai ke dibbe ki tarah hoti hain...\n\nEk baar khula toh sirf ek tukda nahi kha paoge.\n\n(* English Translation:* \"Memories are like a box of sweets... Once it opens, you won't be able to stop at just one piece.\")\n\nWell, here is Baba Palashananda Version of it:\n\nFeatures mithai ke dibbe ki tarah hoti hain...\n\nEk baar scope khula toh sirf ek editor pe nahi rukoge.Poora app hi banaoge...\n\n(* English Translation:* \"Features are like a box of sweets... Once the scope opens up, you won't just stop at a single code editor. You'll end up building the whole damn app...\")\n\nI know Pianists use staff notations, guitarists tabs, ukulelists also use some version of tabs. All the available materials were standardized for these systems. Guitalele is orphaned in this respect.\n\nThe shorthand notation was my own invention. I started with this `0:6@q`\n\n. You won't believe me how easy things look on paper, until you actually implement and use it.\n\nIt was very easy on paper to write `:`\n\n, `@`\n\netc. on paper.\n\nI wrote a `parseToken`\n\nfunction to handle individual notes, then a `parseShorthandText`\n\nfunction to handle the full score. The parser had to deal with:\n\n`Measure 1`\n\n— First measure`3:1@q`\n\n— fret:string@duration`[3:1 | 5:2]@q`\n\n— notes wrapped in brackets`-@q`\n\n— silence with duration`t`\n\nafter duration — hold the note into the next beatThe first version of the parser I implemented could decode the following:\n\n```\nMeasure 1: [0:6 | 2:5]@h -@h\nMeasure 2: 3:3@q | 3:3@q | 4:3@q | 5:3@q\n```\n\nExcited me started typing his very first tab on a system he implemented, a 'King' feel.\n\nIt was not very long, I understood, the system I implemented is utterly problematic to type and utterly confusing as well.\n\nI also missed two major features:\n\n`v1`\n\nfor melody, `v2`\n\nfor bass — independent polyphonic lines, one for chord and another for melodyAlong with those two features, I have to come up with another idea of shorthand. Already 6 hours passed by that time still I am unable to even write a single full score. Even 'Twinkle Twinkle Little Star' felt like a chore to transcribe on my system.\n\nTime for a break. I straight went to terrace and started looking at the clouds. After almost 30 minutes or so when I came back to my desk, the solution was quite clear, why not simply use fret, string notation, exactly how I show it on my channel?\n\nAnd thus the first appearance of:\n\n`3f1sq`\n\n— fret + f + string + s + durationAlmost every parsing was taken care, just had a task of character replacement. I finally did not replace the existing `:`\n\nand `@`\n\nparsing, rather extended it with new syntax.\n\nAlong with this, also removed the mandate of `Measure #`\n\nnotation thus making writing even easier. Now with text based parsing, it also felt easier to actually introduce description. However, as the syntax is space based, I can't put space in the description yet. May be fix for some later time.\n\n`d:text`\n\n— notes to the reader, doesn't affect soundAfter all the features completed, within another hour few more validations entered the scene.\n\nIf a measure has 3.5 beats in a 4/4 time signature, it throws an error. If a string number is out of range (1-6 for Guitalele), it throws an error. If a bracket is missing its closing pair, it throws an error.\n\n``` js\nexport const parseShorthandText = shorthandText => {\n    const lines = shorthandText.split(\"\\n\");\n    const scores = [];\n    const errors = [];\n    let currentScore = null;\n\n    for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {\n        const line = lines[lineIdx].trim();\n        if (!line || line.startsWith(\"==\")) continue;\n\n        if (line.startsWith(\"Score:\")) {\n            if (currentScore) scores.push(currentScore);\n            currentScore = { id: \"\", title: \"\", instrument: \"\",\n                timeSignature: \"4/4\", measures: [], capo: 0 };\n            continue;\n        }\n        // Parse measures, tokens, validate durations...\n    }\n    return { scores, errors };\n};\n```\n\nCompiler design was not part of my syllabus any time. It always felt magical to me how `if`\n\n, `else`\n\n, `for`\n\nconvert into a string of `1`\n\nand `0`\n\n.\n\nBut here I was — writing a lexer, a parser, a validator, and a semantic analyzer for a domain-specific language I had just invented over a weekend. The \"magic\" of compilers didn't feel so magical anymore. It felt like a long chain of `if`\n\nconditions and regex patterns.\n\nOnce the parser could produce structured data, I needed to hear if the output was correct. So I built a note player, a bare-bones audio thing using the Web Audio API.\n\nAt that time, it felt like a toy I got when I was 9. But I did not know at the time that this \"simple toy note player\" would eventually grow into a full SVG-based score viewer with tablature rendering, sheet music notation, voice polyphony, metronome, auto-scroll, and a dark-themed GUI.\n\nBut that story is for the next part.\n\nAt that point, I had a local tool that could parse shorthand, play notes, and display results. I showed the prototype to Gemini.\n\nGemini suggested adding a \"Create\" button.\n\nA Create button won't work without knowing who created it. That needs login. Login means authentication. Auth means a backend. A backend means Firestore. And if I am going through all the trouble of setting up Firestore, why not just save my scores there?\n\nGemini struck a string in me which resonated far longer than I expected. Trade off decision took quite longer than I expected it to be.\n\n**What would I lose?** A few days of effort. Maybe a week.\n\n**What would I gain?**\n\nOh, and a few sleepless nights. Well, three weeks to be very precise.\n\nI made the call. The snowball won't stop rolling.\n\nI just don't know why I chose Firebase. Maybe because, I spend significant office hours working in AWS, so an alternative for home.\n\n`profiles`\n\ncollection for user data.`scores`\n\ncollection with a composite key: `username:instrument:slugified-title`\n\n.\n\n``` js\nconst makeScoreDocId = (uname, instr, title) => {\n  const s = slugify(title);\n  return `${uname}:${slugify(instr)}:${s}`;\n};\n```\n\nThe CRUD operations came next. Firestore made the experience very smooth. No backend needed at all. Simple DB calls, that too direct from client browser. I once thought about it, how nice would it be to directly call DB from UI. Well, turns out Google gave the option, which I was not aware of.\n\nAfter successfully launching the web app I can realistically claim, I have seen the practical limitations of a No-Backend architecture. Trust me, beyond few easy peasy work you absolutely need a strong backend layer.\n\nHere are few things that I leveraged:\n\n`getDocs`\n\nwith a `where`\n\nquery).`setDoc`\n\n).`updateDoc`\n\n).`deleteDoc`\n\n).\n\n``` js\nconst loadScores = useCallback(async () => {\n  if (!user) return;\n  const q = query(scoresRef, where(\"userId\", \"==\", user.uid));\n  const snap = await getDocs(q);\n  // ... build list, sort by creation date\n}, [user]);\n```\n\nThe security rules had to be right too. Readable by all (published scores are public), but writable only by the owner with a verified email. No one should overwrite someone else's score.\n\nIf you are not coming from GCP or Firebase background, Security Rules are simple validations which you can use to limit access to collections and actions on them.\n\nWith the cloud backend in place, I used the tool myself for four days. Wrote a few scores. The first one I published was **Ode to Joy** — a chord melody arrangement for Guitalele. I even made a YouTube Shorts to go with it.\n\nIt was working. People could see it. I could share it.\n\nBut looking at the raw shorthand text in the editor started to hurt my eyes.\n\nRaw shorthand text in a plain textarea is hard to read. All those `3f1sqd:F4_Note`\n\nsilver white tokens blend into a grey soup. After staring at them for days, I needed color. Desperately.\n\nSo I wrote a custom syntax highlighting system from scratch. No libraries. Just regex and inline styles.\n\n``` js\nconst COLOR_SCHEME = {\n  separator: '#334155',\n  headerLabel: '#67e8f9',\n  headerValue: '#e2e8f0',\n  measureLabel: '#c084fc',\n  pipe: '#475569',\n  bracket: '#fbbf24',\n  chordContent: '#fde68a',\n  duration: '#4ade80',\n  voice: '#22d3ee',\n  tie: '#f87171',\n  annotation: '#fb923c',\n};\n```\n\nEvery token type gets its own color. Fret numbers in slate, strings in gray, durations in green, voices in cyan, annotations in orange, ties in red. The textarea sits transparent over a `<pre>`\n\nlayer that renders the highlighted version. You type in invisible text over a colorful display, a trick borrowed from code editors like VS Code.\n\n``` js\nfunction highlightShorthand(text) {\n  const escaped = text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n  const lines = escaped.split('\\n');\n  const coloredLines = lines.map((line) => {\n    if (/^={2,}|^---+/.test(line))\n      return `<span style=\"color:${COLOR_SCHEME.separator}\">${line}</span>`;\n    if (/^(Score|Time Signature|Instrument|Capo|Description|ID):/.test(line))\n      return line.replace(/^(Score|Time Signature|Instrument|Capo|Description|ID):(.*)$/,\n        (_, label, val) => `<span style=\"color:${COLOR_SCHEME.headerLabel}\">${label}</span><span style=\"color:${COLOR_SCHEME.headerValue}\">:${val}</span>`\n      );\n    // ... more token colorization\n  });\n  return coloredLines.join('\\n');\n}\n```\n\nTwo functions and about 80 lines of regex. But those 80 lines make the difference between \"what did I just type?\" and \"oh, I see, the quarter note is missing on beat 3.\"\n\nAlthough I am the only one using it and may be, I would always be the only one using it. Still it feels to put a logical end to anything I start.\n\nA week in, here's what I had built:\n\nAnd none of this was planned. The editor was supposed to be a debugger. The parser was born from boredom. The note player was a validation tool. The cloud backend was an AI's suggestion I took as a dare. The highlighter came from eye strain after publishing my first real score.\n\nAll because I wanted to practice a niche instrument that nobody writes tabs for.\n\nThe snowball hasn't stopped rolling. But that's a story for another day.", "url": "https://wpnews.pro/news/part-1", "canonical_source": "https://dev.to/palash90/part-1-4pdh", "published_at": "2026-06-30 20:09:14+00:00", "updated_at": "2026-06-30 20:18:55.444975+00:00", "lang": "en", "topics": ["developer-tools", "artificial-intelligence"], "entities": ["Guitalele", "React"], "alternates": {"html": "https://wpnews.pro/news/part-1", "markdown": "https://wpnews.pro/news/part-1.md", "text": "https://wpnews.pro/news/part-1.txt", "jsonld": "https://wpnews.pro/news/part-1.jsonld"}}