2026-07-10 12:44:14 +05:30
2026-07-10 12:44:14 +05:30
2026-07-10 12:44:14 +05:30
2026-07-10 12:44:14 +05:30
2026-07-10 12:44:14 +05:30
2026-07-10 12:44:14 +05:30
2026-07-10 12:44:14 +05:30
2026-07-10 12:44:14 +05:30

Interactive guitar tab sample

This is a dependency-free proof of concept: a reusable browser renderer reads a versioned JSON tab file and turns it into an interactive practice sheet.

The provided file is named kesariya.pdf, but the title printed inside it is Haseen (Talwinder). The sample follows the title and music shown in the document. All eight bars are kept in one continuous array, in source order. The license watermark email is intentionally not copied into the web data.

Files

  • index.html - the embeddable page surface
  • styles.css - responsive presentation
  • app.js - generic renderer, JSON loader, bar navigation, practice cursor, metronome, zoom, and keyboard controls
  • haseen.tab.json - the complete source data transcribed from the PDF
  • guitar-tab.schema.json - the reusable guitar-tab/v1 JSON Schema

Run the sample

The page fetches its JSON, so serve this folder instead of opening index.html with a file:// URL:

make

This runs uv run python -m http.server on 127.0.0.1:8080. Override either value when needed, for example make PORT=9000 HOST=0.0.0.0. make serve and make start are equivalent aliases.

Run make help to see all targets and options.

Then open http://localhost:8080.

For the compact iframe view, open http://localhost:8080/?embed=1.

To point the viewer at a hosted compatible file, add an encoded src parameter, for example ?embed=1&src=https%3A%2F%2Fexample.com%2Fsong.tab.json. The JSON host must allow the browser request.

Share through Cloudflare Tunnel

The Makefile defaults to the public hostname tabs.bhakat.dev and a named Cloudflare Tunnel called guitar-tabs. Ensure that bhakat.dev is active in the Cloudflare account used by cloudflared.

Create the tunnel and its DNS route once:

make tunnel-setup

Then, for each sharing session, run:

make tunnel

This starts the local static server and leaves cloudflared in the foreground. Press Ctrl+C to stop both processes. The first command stores the tunnel credential file under ~/.cloudflared; it is not part of this repository.

If IPv6 connections to the Cloudflare edge are unreliable, force IPv4 for the tunnel:

make tunnel TUNNEL_EDGE_IP_VERSION=4

Use the same overrides with both commands to choose another tunnel name or subdomain:

make tunnel-setup TUNNEL_NAME=my-tabs TUNNEL_HOST=guitar-tabs.bhakat.dev
make tunnel TUNNEL_NAME=my-tabs TUNNEL_HOST=guitar-tabs.bhakat.dev

Embed it

<iframe
  src="https://your-site.example/tabs/?embed=1"
  title="Interactive guitar tab"
  loading="lazy"
  style="width: 100%; height: 900px; border: 0"
  allow="autoplay"
></iframe>

The “Copy embed snippet” button generates the equivalent snippet for the current URL.

The standard format

JSON is the canonical interchange format because browsers can load it directly and JSON Schema can validate it. YAML or Markdown can still be used as an authoring format, but should be converted to this JSON shape before rendering.

The hierarchy is:

song
├── metadata + instrument + timing + legend
└── arrangements
    └── sections
        └── blocks
            ├── tab → bars → rows
            └── chordLyrics → lines → spans

Each tab bar declares columnCount and owns one equal-width ASCII row per string. This keeps barlines, line wrapping, and responsive rendering deterministic. Optional column-positioned markers preserve strum arrows and similar annotations without inserting them into the tab grid. Stable IDs and optional beat events provide anchors for future audio sync or note-level highlighting.

Important conventions:

  • Tuning pitches describe open strings before the capo.
  • Fret numbers remain relative to the capo, matching ordinary tab notation.
  • rows[].text contains the inside of the bar; the renderer supplies barlines.
  • Every row in a bar must have the same Unicode-code-point width as columnCount.
  • markers[].atColumn is zero-based within that bar-local row.
  • timeSignature, bar beat counts, note events, repeats, and chord/lyric blocks are optional when the source does not provide them.
  • extensions is the escape hatch for namespaced vendor data.

The semantic checks in app.js are a normative second validation pass for rules JSON Schema cannot express across sibling values. They enforce unique IDs, columnCount and row-width agreement, tuning/string consistency, legend references, and in-range text, event, note, and marker positions. A production ingestion pipeline should run the JSON Schema first and equivalent semantic checks second.

Interaction included in the sample

  • Read all eight source bars as one continuous tab
  • Click a bar or use arrow keys to focus it
  • Start a BPM-controlled 4-count practice cursor and optional metronome
  • Loop the current tab
  • Resize the tab without losing alignment
  • Load another compatible .json file from the browser
  • Use keyboard shortcuts: Space, , , M, and L

The source PDF states 82 BPM but does not state a time signature or exact beat mapping. The sample labels its 4-count cursor as a practice aid rather than presenting it as transcribed rhythm.

Description
No description provided
Readme 50 KiB
Languages
JavaScript 54.7%
CSS 30.7%
HTML 11.6%
Makefile 3%