TypeScript / JavaScript engine for engraving sheet-music and tablature in the browser.
VexFlow renders music notation to Canvas, SVG, or WebGL. It powers notation editors, education apps, and real-time visualisers
// draw a single quarter-note middle-C
import { Renderer, Stave, StaveNote, Formatter, Voice } from "vexflow";
const div = document.getElementById("score");
const renderer = new Renderer(div, Renderer.Backends.SVG);
const context = renderer.getContext();
const stave = new Stave(10, 40, 250).addClef("treble").setContext(context).draw();
const notes = [ new StaveNote({ keys: ["c/4"], duration: "q" }) ];
new Formatter().joinVoices([new Voice({num_beats:1,beat_value:4}).addTickables(notes)]).formatToStave(notes, stave);
NPM for bundlers or a quick CDN script tag.
NPM / PNPM / Yarn
npm i vexflow # current stable (4.2.x)
npm i vexflow@next # dev build (5.x branch)
ESM / TS Import
import { Renderer, Stave } from "vexflow";
CDN (UNPKG)
<script src="https://unpkg.com/vexflow@4.2.5/build/cjs/vexflow-min.js"></script>
👉 The global object is Vex when using the classic UMD build.
Everything in VexFlow is built from five primitives.
Manages the drawing backend (SVG, Canvas, or WebGL) and produces a graphics context.
Defines a staff system: clef, key-signature, time-signature, barlines, tempo text.
StaveNote, TabNote, Articulation, Accidental, ClefNote — each inherits from Tickable and owns glyph metrics.
Groups notes that share rhythmic context, enforcing complete beats per bar.
Calculates horizontal spacing, collision-avoidance, and justifies voices across a stave.
From single notes to full scores.
Accidentals — call addAccidental(index,new Accidental("#")); be sure to add before formatting.
Beams — group notes via new Beam(notes), then render after voices are formatted.
Ties & Slurs — StaveTie handles tied durations; Curve draws slurs between arbitrary tickables.
Tuplets — pass grouped notes to new Tuplet(notes, options).
EasyScore API — for quick demos: factory.EasyScore().voice(score.parse("C#5/q, D5, E5, F5")) renders with one-liner simplicity.
Hybrid notation and linked voices.
A TabStave lines-up with a regular stave via System helper. Use ModifierContext to share beams or Bend/ Vibrato glyphs between note types.
Mouse-over highlights, playhead cursors, and real-time updates.
// highlight note under cursor
renderer.getContext().svg.addEventListener("mousemove", e=>{
const { offsetX:x, offsetY:y } = e;
notes.forEach(n=> n.setStyle({ fill: n.containsPoint(x,y) ? "#ffa500" : "#000" }));
renderer.draw(); // re-paint SVG
});
SVG backend allows DOM-level hit-testing; Canvas needs client-side mapping.
Keep FPS stable in dynamic editors.
Batch glyph generation with a Factory singleton; re-use Glyph objects across redraws.
Turn off debug bounding-boxes (Vex.Flow.DEBUG = false) in production.
Prefer the SVG backend for frequent layout edits; switch to Canvas for static high-volume scores.
Major API shifts and new features.
Version | Feature | Notes |
---|---|---|
4.2 | TypeScript Rewrite | Typed definitions ship with package; better intellisense |
4.2 | Factory.System | Simpler multi-stave layout helper. |
4.1 | EasyScore grace-notes | /grace syntax for quick embellishments. |
5.0-beta | WebGL Renderer | GPU-accelerated vector glyphs; orders-of-magnitude faster when animating scores. |
5.0-beta | Variable-width beams | Auto-taper based on stem direction. |
Docs, samples, and helper projects.
GitHub Repository (issues, discussions, nightly builds)
VexFlow Examples Repo for ready-made code snippets
Swipe horizontally to explore real VexFlow output.