The sprite model of mScore.js
For performance reasons most graphics rendering of mScore.js is performed by copying pre-rendered sprites onto the canvas. These are stored in an
mScore.Renderer
object, which is usually created once on loading a web page, then used to render all musical materials on that page.
The process of creating an mScore.js renderer is as follows:
var myRenderer = new mScore.Renderer();
/* Code to set renderer parameters / modify sprite descriptions goes here */
myRenderer.Const.lineWidth = 12; // for example: set width between two lines of a stave to 12px;
// lineWidth is the most common parameter to set.
myRenderer.initialize();
Creating the renderer object (line 1 above) sets the renderer's constants and creates its sprite description objects (both stored in
Renderer.Const
). These can afterwards be modified at will. Finally a call of Renderer.initialize()
renders
the sprite descriptions into sprites. Adter this the renderer can be used to display content. It is strongly discouraged to modify mScore.js’s source
files themselves to modify sprite descriptions. Please use the mechanism described here to do that.
All sprites use Renderer.Const.lineWidth
as their basic length unit, all other lengths are stored as multiples of it.
Renderer.Const.r
is the (half) thickness of stave lines, note stems etc. Usually these two parameters are the only ones that need to
be modified.
Parameter | default | Description |
---|---|---|
lineWidth | px | Central unit. Height of the whole stave is obviously 4·lineWidth . Must be an even
number, so that notes of all pitches can be drawn at whole pixel positions. |
r | Half thickness of stave lines, note stems and bar lines. Given as a multiple of lineWidth (like all other
lengths unless mentioned). | |
staveShift | px | Pixel amount by which the center of stave lines is shifted vertically (> 0 = downwards) away from whole pixels. Relevant for visual display (smoothing) at smaller sizes. Use values between −1 and 1. |
barlineShift | px | Pixel amount by which the center of bar lines is shifted horizontally (> 0 = right) away from whole pixels. Relevant for visual display (smoothing) at smaller sizes. Use values between −1 and 1. |
toB)
toB)
toT)
toT)
The mScore.js sprite path language
mScore.js uses its own vector graphics language to describe sprite outlines, which is broadly derived from SVG paths. The language is designed to be very concise and easy to parse while retaining reasonable human readability.
An mScore.js sprite path consists of operators (listed below) followed by the appropriate number of numerical arguments. If operators
of the same type follow each other (often the case with L
or C
) the operator needs only be called once,
it stays on until a different one is called (as in SVG). The following two code snippets (drawing a triangle) are equivalent:
M -11 3 L 11 3 L 0 -15 Z
≅M -11 3 L 11 3 0 -15 Z
Spaces in the code can be omitted wherever they are not necessary: around operators or in front of numbers beginning with a “−”. The triangle code shown above can be shortened to
M-11 3L11 3 0-15Z
Spaces between positive numbers must remain, of course. There often is the case that a path consists of long sequences of numbers (coordinates) which are
(signed) integers with a maximum number of digits. In these cases some more space can be saved by using the integer sequence operators
D
and X
. Each one of them is immediately followed by a single digit which specifies the number of places in
each of the integers following it. These integers can now be written without spaces between them (padding shorter numbers with zeros). An integer sequence
can also (and usually does) contain operators. It ends at the end of the input, at a space, a different integer sequence, or at an
evaluating expression. D
takes decimal numbers, X
takes hexadecimal numbers.
Note that hexadecimal digits a–f must be lowercase so they don't collide with operators.
For example, in the triangle path from above no number has more than two digits; they can even be written with a single hexadecimal digit each, so we equivalently get
D2M-1103L110300-15Z
≅X1M-b3Lb30-fZ
The path design helper tool provides a button to “compress” paths to a chosen integer format; usually 2 unsigned hexadecimal digits will be enough for reasonable visual accuracy, except with circular arcs, which therefore remain with floating-point coordinates.
Code | Operator | #Args | Description |
---|---|---|---|
Transformation operators | |||
Rst | Reset transformation | 0 | Resets the current transformation to the initial value |
Sc | Scale coordinates | 2 | The path design tool only works with uniform scalings and reflections, but sprites also accept non-uniform scalings. |
Trs | Translate coordinates | 2 | Calls canvas’ translate |
Trp | Translate to pivot | 0 | Resets scaling and translates the origin to the pivot. Thus the command is equivalent to
RstTrs@%Cen.x@@%Cen.y@ . Will often be the first command of a sprite path. |
Trc | Translate to corner | 0 | Resets scaling and translates the origin to the top left corner of specified bounding
box. Thus the command is equivalent to RstTrs@%Crn.x@@%Crn.y@ . Will often be the first command of a
sprite path. |
Tfl | Linear transformation | 4 | Transforms the x axis into the vector given by the first two arguments, and the y axis into the vector of the last two arguments. Use this if you need a skew transformation. |
Path construction operators | |||
M | Move to | 2 | Calls canvas’ moveTo |
L | Line to | 2 | Calls canvas’ lineTo |
Q | Quadratic Bézier curve to | 4 | Calls canvas’ quadraticCurveTo |
C | Cubic Bézier curve to | 6 | Calls canvas’ bezierCurveTo |
Ap | CCW circular arc | 5 | Arguments are xcenter, ycenter, radius, start angle, end angle (in degrees). |
An | CW circular arc | 5 | Arguments are xcenter, ycenter, radius, start angle, end angle (in degrees). |
Misc. operators | |||
Z | Close path | 0 | Calls canvas’ closePath |
Rct | Construct rectangle | 4 | As with canvas’ rect , the third and fourth arguments are the
rectangle’s width and height |
Af | Construct circle | 3 | Construct a full circle, arguments are xcenter, ycenter and radius. |
Str | Stroke path | 1 | Normally constructed paths are filled after all code has been processed. For special effects you can use this command to stroke what has been constructed so far, then start a new path. The argument is the line width to use. |
As a practical example, here is the code for the G clef displayed above:
Sprite description objects and evaluating expressions
The sprite path language described so far can be used without creating/using an mScore.js renderer or a sprite description object. For example
var CT = document.getElementById('myCanvas')).getContext('2d');
CT.beginPath();
mScore.drawSpritePath.call(CT, 'X1M-b3Lb30-fZ'); // static function of mScore.Renderer
CT.fill();
If we do use a sprite description object to render a sprite, we gain the ability to use evaluating expressions in sprite paths. They are snippets of
JavaScript code that can be evaluated (by JavaScript’s eval
function) to a numerical value which is then used like any explicit
numeric input. They are enclosed by $...$
, @...@
or !...!
. The difference is how
the evaluated number is interpreted:
$...$ | The evaluated number is used as-is, i.e. in current local coordinates. |
@...@ | The evaluated number divided by the sprite’s scale value. I.e. if no further explicit
Sc (scaling) has been applied, it will be a pixel coordinate. |
!...! | If no further scaling has been applied, the number will be taken to be multiples of
Renderer.Const.lineWidth . |
The purpose of evaluating expressions is to use parameters of the sprite description and constants of the renderer (in Renderer.Const
).
Because sprite descriptions are constructed with a JavaScript prototype chain ending in Renderer.Const
, all of these values can be
addressed by this.myVariableName
. To shorten this a bit and improve readability we allow addressing variables by
%myVariableName
. (%
will simply be replaced by this.
on rendering). If you need the
modulo operator, simply put a space after it, that will prevent the replacement.
As an example, here is code to draw the head of a quarter note with a stub of a stem pointing upwards.
TrpSlc0 0$%R.x$$%R.y$$%R.lam$0$%a0$ L$%R.x-2*%r$@-2*%Pvt.y@ $%R.x$@-2*%Pvt.y@ Z