Rainbow Ripple, how do you ripple?
by David JonesRainbow Ripple Matrix Mono is my first experiment with making a COLRv0
font.
The font combines COLR
technology with selectable colourways and
smart rules to create a harmonised colour change across
the text.
I call this the Ripple System.
The graphic form of the font is from the Epson FX-80 dot matrix printer.
Here’s an example of the font in a box that you should be able to edit:
Rainbow Ripple Matrix Mono
Get Ready to put some Rainbow Ripple in your typography!
The experimental nature of this font (and mostly the tools used to make it) meant that for about 1 year it had a bug where it didn’t display on macOS Firefox Desktop browser, but did everywhere else; macOS Firefox Desktop is, of course, my main browser. That bug should be fixed (short version: do not lie about your glyph bounding boxes), but do let me know if there are problems.
The font is in a preview and testing stage and there are a couple of other issues to fix as well: the left side-bearing and the somewhat limited character repertoire.
All the glyphs are made from dots that lie on a particular grid; this is in some ways similar to pixel fonts.
Coloured dots
In this font each dot is coloured and the colours are chosen
to give a rainbow colouring that ripples across the letters.
Each dot has a single flat colour and they are chosen from
a small palette;
this suits the OpenType COLRv0
format very well.
COLRv0
was designed to be simple to implement with the
existing font rendering systems:
Build up a a colour glyph by rendering a sequence of layers
with each layer rendered in a single colour.
That is more or less how
the COLR
(version 0) table works:
a colour glyph has an entry in the COLR
table and
consists of a number of layers;
each layer has a single colour and a shape.
The shape of a layer is defined by reference to another glyph in the font file.
A relatively simple rendering system can be made by taking each layer in turn and rendering the layer glyph in its colour into a buffer and repeating until all the layers are rendered.
I’ll illustrate that with R from this font. It breaks down into these pieces:
There may be empty layers, depending on the letter shape
and the repeating tile shape.
In this case there is 1 that is empty.
In the current implementation empty layers are still defined
'ecause technically it’s easier to keep them than remove them
(the extra space that these empty layers take up in the font file is
dominated by the UTF-16 encoding of the glyph name).
Even /space
is broken down into several layer glyphs, all of which
are empty. Hmm.
Let’s ripple
Ready for Rainbow Ripple? Try Ripple Today!
If you look at the R in this example you can see that the separate occurences of R have the same shape, but different colour. Different colourways, to borrow a term from the textile industry. At least, they will have different colourways if they appear at different positions along the line. On a narrow mobile device you may be unlucky and have all the Rs start at the beginning of a line. In that case either edit the text, rotate your phone to landscape, or pretend that i was talking about the ps in «Ripple» instead.
I could have made one colourway per glyph and
it would have made a nice COLRv0
font.
But i was hooked and had to carry on.
Within each glyph the colours appear in sequence from left to right in a cyclic ripple that is the same across all glyphs. These rainbow examples use a palette of 7 colours, approximating a rainbow (that repeats). The colours themselves i re-used from Kate’s 12-bit rainbow palette.
That's neat. But note that the same rainbow cycle ripples across the letters consistently. The gaps between letters can make it hard to follow, because the palette still cycles but you can’t see it in the gap. It’s a little bit clearer using a mixture of H and N:
HNHHNNHNHNHHNNHNHNHHNNHNHNHHNNHNHNHHNNHNHNHHNNHN NHNNHHNHNHNNHHNHNHNNHHNHNHNNHHNHNHNNHHNHNHNNHHNH
Each colour glyph has one layer for each colour that appears in
the glyph and the COLR
table defines the colour and the shape.
On a technical level, each colourway of each glyph is
a new colour glyph that has a separate entry in the COLR
table.
Fortunately, the layer glyphs that define shape can be re-used (for R we can see those in the breakdown above). All the colourways of R have the same number of layers and the same shapes, they are just coloured differently.
The glyphs that we start with in the text are uncoloured: a plain R, a plain a, and so on;
i call these primary glyphs.
They are directly mapped from unicode values (cmap
table) and
represent the uncoloured glyph shape.
For each primary glyph we have 7 colour glyphs, one for each colourway; and for each colourway we have 5 layer glyphs that define the geometry for a single colour (some of which may be empty, some of which may use the same colour as another layer, again for technical convenience). As i said, i re-use the layer glyphs for all the colourways of a primary. But that’s still 12 more glyphs for each primary, so a ×13 expansion.
The numbers can be fiddled a bit. The number of colourways depends on the palette and the shape of the repeating tile. The number of layer glyph shapes depends on the shape of the repeating tile.
In both cases the current Ripple System implementation has a saving of between 50 to 88% because the tile repeat "divides into" the width of all the glyphs (this is a mono font, so they are all the same). This saves in the number of colourways required. A more general set of widths for the glyph in the font would result in more colourways and another expansion in glyph numbers.
The magic of colour
Having 7 different colourways for each glyph doesn’t by itself do anything. In so far as fonts are magic (and they are), that magic has been created with...
OpenType Layout feature code.
Typically OpenType Layout feature code is used for things like
contextual selection of alternate glyphs: for example when /f
is followed by /b
in korfball a feature code could be
implemented to replaced /f
with an f that does not collide
with the /b
, /f.calt
say.
Or we could replace /f/b
with a ligature /f_b
.
The OpenType Layout features are also used much more extensively
in so-called complex scripts (Arabic, Devanagari and so on).
The same feature code that gives a font contextual alternatives can be used to select colour glyphs.
NNNNNNNNN
Consider this row of N glyphs. Each particular N starts and ends with a certain colour, and, by extending the ripple to the next glyph, determines the colourway of the next glyph.
I happen to know that the first glyph in this sequence
is called u004E.p0
;
u004E
stands for unicode 004E which is LATIN CAPITAL LETTER N,
and in Rainbow Ripple .p0
means use colourway 0.
There is an OpenType feature rule of the form:
sub u004E.p0 u004E' by u004E.p12;
The prime (it’s an ASCII apostrophe really) in u004E'
means that this
rule is a contextual rule, and that u004E
is the input
(the rule is applied when the input matches).
A contextual rule may use context before (and after, but in this case,
just before) the input to decide what to do with the input.
The input is marked with a prime in the rule.
So u004E
when preceded by u004E.p0
is substitued with u004E.p12
(the preceding context is unchanged).
This is exactly the situation that the second N finds itself in.
It is preceeded by u004E.p0
so the second N will get substituted by
u004E.p12
.
There are rules for every possible combination of primary glyph preceded by each colour glyph. In actual practice groups are used to keep the number of rules to a manageable level. In the feature file as defined, most of the rules are remarkably simple:
sub @next.p12 @primary' by @init.p12;
The @
syntax refers to a named group. @primary
contains all the
primary glyphs in sequence; @init.p12
contains the same glyphs
in the same sequence, but in their p12
colourway.
The @next.p12
group is all the glyphs whose colourway determines that
the next glyph should be colourway p12
.
For this rainbow ripple pattern, only 7 rules are needed.
The appropriate definition of the groups is
A Small Matter of Programming.
In this way every single possible colour glyph will determine which colourway to use for the following primary.
At the beginning of a line (or paragraph, or whatever segment of text the font engine chooses), we need to prime the colour pump. Picking a colour glyph for the first glyph will cause all of the following glyphs to be coloured, but without that, nothing will change.
I have a set of rules used only at the start of line.
I have all sort of ideas here for magically and seemingly randomly choosing the starting colourway based on following context, but the prototype Ripple System did the simplest thing that could possibly work and picked the first colourway. That was not only sufficient, but for certain patterns gave a quite pleasing coherence of colour pattern across a whole page.
Those rules look like:
sub u004E' by u004E.p0;
This too is a contextual rule, note the prime in u004E
,
but without any context used. This is for technical reasons to do with
lookup rule priority: in the middle of a line, it is important that the
earlier rules are used, and that this rule is only applied at the
beginning of a line.
Endless forms, most colourful
Of course all of the above is done with coding.
In fact this font has never been touched with a “normal” font editor
like FontForge or Glyphs.
The font is the product of my own scripts in Python and font processing tools
(mostly in Go), with the exception of makeotf
used to compile the
feature rules.
Even the SVG for the dot was written by hand in a text
editor.
The Ripple System scripts are sufficiently flexible that i can try many variations of tile shape and palette. I show a few here.
Rainbow Ripple Matrix Mono 102
Rainbow Ripple Matrix Mono 104
Rainbow Ripple Matrix Mono 106
Rainbow Ripple Matrix Mono 194
Rainbow Ripple Matrix Mono 196
Rainbow Ripple Matrix Mono 302
Rainbow Ripple Matrix Mono 412
Rainbow Ripple Matrix Mono 492
I even made a cool Univers/Bitstream style numbering system to save me making names for what turns out to be a near infinite number of variations.
Put a rainbow in your life now!