HTML5: Typographic Metrics

Typography support between browsers has a history of being spotty.  One of the major hurdles in creating the (yet-to-be-released) next-incarnation of Sketchpad was typographic support; between browsers and even within the <canvas> specs.  One pitfall of the <canvas> tag is the lack of text-metrics support (past calculating the width via ctx.measureText).  This prevents us from emulating how text works in DOM for other elements such as <div> or even <textarea>—fortunately, these metrics can be measured through simple css-hacks.The solutions to the following are provided:
  • ctx.textBaseline=”alphabetic”; // alphabetic, top, bottom, middle
  • ctx. textAlign=”left”; // left, right, middle, start, end
  • measureText(‘hello’).width // px width of typeface

The solution for finding these metrics is compatible with browsers as far back as 2004 (such as Firefox 1.0), before <canvas> was introduced.  Out of 64 browsers tested on BrowserShots and the 30 tested on CrossBrowserShots, four browsers failed to generate proper metrics for Arial with these CSS solutions.  There was an error margin of +/- 2px, which may accounted for in the differences between anti-aliasing in <canvas> vs. rendering in <div>/<span>/<ect>.

Table of contents

Bounding boxes

Calculating the bounding-box of unicode

I’ve used a simple brute-force formula to calculate the bounding-box of unicode characters for the following text experiments called “getTextMetrics”.  Once you know the text metrics, you can do most anything ?

It’s been very useful to me for many purposes:  Unicode Profiling Project, Font-Family Profiling Project, calculating the “Ascent, Descent, and x-height”, and finding the bounding-box of unicode glyphs to create stamps, amongst other things.

See getBitmapBounds demo in action.

(function() {
var fontFamily = "Arial, san-serif";
var fontSize = 14;
getFontMetrics = function(props) {
	var ctx = props.ctx;
	var text = props.text;
	var bboxHeight = props.bboxHeight;
	var canvasHeight = props.canvasHeight;
	var baseline = props.baseline || "alphabetic";
	var flip = props.flip || false;
	var drawBaseline = props.drawBaseline || false;
	if (props.fontFamily) fontFamily = props.fontFamily;
	if (props.fontSize) fontSize = props.fontSize;
	// setting up the canvas
	ctx.save(); // create canvas to use as buffer
	ctx.font = fontSize + "px " + fontFamily;
	var textWidth = ctx.measureText(text).width;
	// This keeps font in-screen, measureText().width doesn't
	// quite do it in some cases. For instance "j", or the letter "f"
	// in the font "Zapfino".
	var offsetx = fontSize * 2;
	var offsety = fontSize * 2;
	var cwidth = ctx.canvas.width = Math.round(textWidth + offsetx * 2);
	var cheight = ctx.canvas.height = canvasHeight ? canvasHeight : Math.round(offsety * 2);
	if (typeof(baseline) == "string") {
		offsety = 0; // using <canvas> baseline
 ctx.textBaseline = baseline;
 }
 // ctx.font has to be called twice because resetting the size resets the state
 if (flip) ctx.scale(1, -1);
 ctx.font = fontSize + "px " + fontFamily
 ctx.fillText(text, offsetx, (typeof(bboxHeight)=="number" ? bboxHeight : offsety));
 // drawing baseline
 if (drawBaseline) {
 ctx.fillRect(0, canvasHeight/2, ctx.canvas.width, 1);
 }
 // grabbing image data
 var imageData = ctx.getImageData(0, 0, cwidth, cheight);
 var data = imageData.data;
 // calculating top
 var top = 0;
 var pos = 0;
 while (pos < data.length) { if (data[pos + 3]) { pos -= pos % (cwidth * 4); // back to beginning of the line top = (pos / 4) / cwidth; // calculate pixel position top -= offsety - fontSize; pos = data.length; // exit loop } pos += 4; } // calculating bottom var bottom = 0; var pos = data.length; while (pos > 0) {
 if (data[pos + 3]) {
 pos -= pos % (cwidth * 4); // back to beginning of the line
 bottom = (pos / 4) / cwidth;
 bottom -= offsety - fontSize;
 pos = 0; // exit loop
 }
 pos -= 4;
 }
 // calculating left
 var left = 0;
 var col = 0, row = 0; // left bounds
 while (row < cheight && col < cwidth) {
 var px = data[(row * cwidth * 4) + (col * 4) + 3];
 if (px) {
 left = col - offsetx;
 row = cheight;
 col = cwidth;
 }
 row ++;
 if (row % cheight == 0) {
 row = 0;
 col++;
 }
 }
 // calculating right
 var right = 0;
 var col = cwidth, row = 0; // right bounds
 while (row < cheight && col > 0) {
 if (data[(row * cwidth * 4) + (col * 4) + 3]) {
 right = col - offsetx;
 row = cheight;
 col = cwidth;
 }
 row ++;
 if (row % cheight == 0) {
 row = 0;
 col --;
 }
 }
 // calculating real-bottom
 var realBottom = 0;
 var pos = data.length;
 while (pos > 0) {
 if (data[pos + 3]) {
 pos -= pos % (cwidth * 4); // back to beginning of the line
 realBottom = (pos / 4) / cwidth;
 pos = 0; // exit loop
 }
 pos -= 4;
 }
 // restoring state
 ctx.restore();
 // returning raw-metrics
 return {
 "left": (-left),
 "top": (fontSize - top),
 "width": (right - left),
 "height": (bottom - top),
 "bottom": realBottom
 }
};
})();

ctx.textBaseline

Mimicking the way text is supported in other DOM elements

By default <canvas> text is aligned to the “alphabetic” baseline.  When drawing to a canvas, using the default baseline, and a y-position of 0, only the descenders can be seen:

ctx.fillText(“Hello world!”, 0, 0);

To mimic how text is viewed in <div>/<span>/<input> other html elements we can use the “top” baseline.  This works wonderfully, however, not all browsers support the “top” baseline. If baseline=”top” works in your browser “Hello world!” should become visible in the following example:

ctx.textBaseline = “top”;
ctx.fillText(“Hello world!”, 0, 0);

Opera, Safari and Chrome work identically with “top”, “bottom”, “middle”, and “alphabetic”… however, Firefox seems to remove the “buffer-spacing” (the invisible spacing that goes above the ascenders, and below the descenders on each line of text) which prevents it from mimicking how Firefox draws other DOM elements.  Older browsers wont support baseline=”top” or other baseline variants at all.

This illustrates our problem… lets find a solution!

<canvas> -> “Ascent”, “Descent” and “x-height”

According the Apache FOP, the “top” baseline is equal to the “ascent”, the “bottom” basline is equal to the “descent”, and the “middle” baseline is half of the “x-height”.  Given this information, it’s possible to measure these values in <canvas> when ctx.getImageData() and ctx.textBaseline are supported.

To measure these baselines, the following code was used:

See <canvas> baseline demo in action.

// finding portion that protrudes past bottom of alphabetic baseline
var Descent = getFontMetrics({
	ctx: ctx,
	text: "YourText",
	bboxHeight: 0,
	canvasHeight: bboxHeight * 3,
	baseline: "alphabetic",
	fontFamily: fontFamily + ", " + defaultFont,
	fontSize: fontSize
}).bottom;
// calculating top-baseline
var TopBaseline = getFontMetrics({
	ctx: ctx,
	text: "YourText",
	bboxHeight: 0,
	canvasHeight: bboxHeight * 3,
	baseline: "top"
}).bottom - Descent;
// calculating bottom-baseline
var BottomBaseline = getFontMetrics({
	ctx: ctx,
	text: "YourText",
	bboxHeight: bboxHeight,
	canvasHeight: bboxHeight * 3,
	baseline: "bottom"
}).bottom - bboxHeight - Descent;
// calculating middle-baseline
var MiddleBaseline = getFontMetrics({
	ctx: ctx,
	text: "YourText",
	bboxHeight: bboxHeight,
	canvasHeight: bboxHeight * 3,
	baseline: "middle"
}).bottom - bboxHeight - Descent;

Unfortunately, the only reliable baseline identically supported between all browsers in <canvas> is “alphabetic”, and there is no way to measure the “ascent” and “descent” when the only working baseline is “alphabetic”.  The reason is the invisible “buffer-spacing” above and below the descenders changes the expected output, and there is no way to measure “invisible” space.

To recap: at this point, we still don’t have the ability to measure the “ascent”, “descent” and “x-height” when textBaseline isn’t supporting “top” or “bottom”… but, we do now know what values we’re searching for.  Next, let’s look into getting the values from CSS.

CSS -> “Ascent”, “Descent” and “x-height”

After searching for a solution for hours in <canvas> under the crazy pursuit of measuring something invisible, I came home and found a simple solution in CSS. By using an <img> (or any inline-block element) and the vertical-align property inside of a container element (such as a <div>), the values <canvas> provides for ctx.textBaseline can be matched with an error margin of +/- 2px—many fonts are matched exactly.  Likely, these discrepancies are due to anti-aliasing in <canvas> vs. DOM.

  1. The “top” baseline is equal to the image.offsetTop since an <img> element is automatically aligned to the baseline of a font.
  2. The “bottom” baseline can be found by subtracting the height of the text (see the measureText section) from the “top” baseline.
  3. The “middle” baseline can be found using “line-height: 0” on the container element and measuring the image.offsetTop—this works, because “line-height: 0” aligns the image to the center of the text, as the text now has a height of 0.  In order to get the proper values the whole experiment must be offset vertically (so the text isn’t hidden off-screen).

See CSS text-metrics demo in action.
See text baseline demo in action.

// setting up html used for measuring text-metrics
var container = document.getElementById("container");
var parent = document.createElement("div");
var image = document.createElement("img");
image.width = 42;
image.height = 1;
image.src = "./media/1x1.png";
parent.appendChild(document.createTextNode("TheQuickBrownFox!"));
parent.appendChild(image);
container.appendChild(parent);
// getting css equivalent of ctx.measureText()
image.style.display = "none";
parent.style.display = "inline";
var measureHeight = parent.offsetHeight;
var measureWidth = parent.offsetWidth;
// making sure super-wide text stays in-bounds
image.style.display = "inline";
var forceWidth = measureWidth + image.offsetWidth;
// capturing the "top" and "bottom" baseline
parent.style.cssText = "margin: 50px 0; display: block; width: " + forceWidth + "px";
var TopCSS = image.offsetTop - 49;
var HeightCSS = parent.offsetHeight;
var BottomCSS = TopCSS - HeightCSS;
// capturing the "middle" baseline
parent.style.cssText = "line-height: 0; display: block; width: " + forceWidth + "px";
var MiddleCSS = image.offsetTop + 1;

 


Compensating for “overhanging” and “clipping”

Some font-faces protrude past the outside of their em-box, clipping their x or y axis.  This protrusion, in typography, is called an overhang (or overshoot).  Fortunately, this is something that can be compensated for.

To fix the clipping on the top portion of the text:

  1. Measure the entire height of the font in <canvas> using a lot of padding to make sure the entire text-string is visible.
  2. Resize the <canvas> to the height of the em-box, and align the baseline of your text to “top”.  Measure the entire height again.
  3. Subject the first value from the second, this difference is the amount the text protrudes past the top of the em-box.
  4. Using this value, the top portion of the font-face that was clipped can become visible.  This can be applied to <div>’s using padding-top, or to <canvas>’s by adjusting the y-offset of a text drawing command such as ctx.fillText() and adding the same value to the height of the <canvas> (assuming we want a perfect bounding-box).

Here’s an example of finding the clipping on left and top:

// compensating for text-clipping using padding-left
var leftClipping = getFontMetrics({
	ctx: ctx,
	text: "YourText"
}).left;
if (leftClipping < 0) { // is padding, not clipping
	leftClipping = 0;
}
// compensating for text-clipping using padding-top
var topClipping = getFontMetrics({
	ctx: ctx,
	text: "YourText",
	bboxHeight: 25,
	canvasHeight: bboxHeight,
	baseline: "top"
}).top + 25;
if (topClipping < 0) { // is padding, not clipping
	topClipping = 0;
}

Native support between browsers

On a side-note, it looks like at this time no browser supports the HTML5 standards “ideographic” or “hanging”.  Opera/Safari/Chrome default to “top” and “bottom” respectively, whereas Firefox defaults to “alphabetic” in when these options are chosen.  It looks impossible to fix “ideographic” and “hanging” without these values being provided via an external means (i.e. Python)—please prove me wrong ?

ctx.textAlign

Fixing older browsers

The only align that works in all browsers, old and new, is ctx.textAlign=”left”.  We can fix “right” and “center” in older browsers by calculating the text-width.  This can be done simply by creating a <span> with the same font-properties, and measuring the span.offsetWidth (DOM level 0).

Align to “start” and “end” require the additional knowledge of the directionality of the font to distinguish RTL from LTR languages.  This poses a problem if the website doesn’t specify this information in the

See textAlign demo in action.

<span id="control">Hello world!</span>

 

function getAlign(text, type, offsetx) {
	var direction = window.getComputedStyle(document.body)["direction"];
	control.textContent = text;
	switch(type) {
		case "left": break;
		case "start": offsetx -= (direction == 'ltr') ? 0 : control.offsetWidth; break;
		case "end": offsetx -= (direction == 'ltr') ? control.offsetWidth : 0; break;
		case "right": offsetx -= control.offsetWidth; break;
		case "center": offsetx -= control.offsetWidth / 2; break;
	}
	return offsetx;
};

Native support between browsers

Opera, Safari, Chrome and Firefox work identically with “left”, “start”, “end”, “right” and “center” in their latest branches.  Older browsers such as Firefox 2.x, and Opera 9.x require a fallback in order for ctx.textAlign to work properly, such as this CSS solution.

ctx.measureText

Fixing older browsers

We can fix old browsers with broken ctx.measureText support in CSS by calculating the span.offsetWidth using the same methods we used to fix ctx.textAlign:

See measureText demo in action.

<span id="control">Hello world!</span>

 

function measureText(text) {
	control.style.display = "inline";
	control.textContent = text;
	return {
		height: control.offsetHeight,
		width: control.offsetWidth
	};
};

Measurements provided this CSS solution produces identical results as ctx.measureText in Safari, Chrome and Opera. Firefox results are identical most of the time, with the occasional erroneous result within +/- 1px.  Zapfino is an example of a font-face that produces this deviance.

Native support between browsers

Opera, Safari, Chrome and Firefox work nearly identically across systems within a difference of +/- 1px. Older browsers such as Firefox 2.x, and Opera 9.x require a fallback in order for ctx.measureText() to work at all.

Line-breaks in <canvas>

Finding the “em-height”

The em-height of a font can be found in Opera, Safari, Chrome and Firefox through measuring CSS-positioning in a similar method to the ctx.textAlign and ctx.measureText() demos.

Create a <span> element with the text you want to measure, with the font-properties set how you want them, and run span.offsetHeight—this measures the em-height.  The em-height is the value we’re going to use to offset our line-breaks.

Calculating line & letter breaks

Calculating word-wrapping entails looping through the text.split(“ “) into singular words, and measuring each word individually until the edge of the bounding-box is hit, at which point a break is inserted, and the process continues.   This works great for words that don’t extend past the bounding-box on their own.

When the word is so large it extends past the bounding box, we need to add in “letter-wrapping”.  This is similar to hyphenation, but without the hyphen ? Calculating letter-wrapping entails looping through the text.length of the long word in question, until the edge of the bounding-box is hit, at which point a line-break is added.

See lineBreaks demo in action.

function getLines(text, maxWidth) {
	var returns = text.split("n");
	var lines = [];
	var lastPhrase = "";
	function splitWord() {
		var width = measureText(lastPhrase).width;
		var posA = 0;
		var posZ = 0;
		if (width > maxWidth) {
			for (var n = 0, length = lastPhrase.length; n < length; n ++) { 				var width = measureText(lastPhrase.substr(posA, posZ ++)).width; 				if (width > maxWidth) {
					lines.push(lastPhrase.substr(posA, posZ - 2));
					posA = n - 1;
					posZ = 2;
				}
			}
			return lastPhrase.substr(posA, posZ + 2);
		}
	};
	for (var n = 0; n < returns.length; n++) {
		if (lastPhrase) lines.push(lastPhrase);
		var phrase = returns[n];
		var spaces = phrase.split(" ");
		var lastPhrase = "";
		for (var i = 0; i < spaces.length; i++) {
			var measure = measureText(lastPhrase + " " + spaces[i]).width;
			if (measure < maxWidth) { 				lastPhrase += ((lastPhrase ? " " : "") + spaces[i]); 			} else { 				if (measure > maxWidth) {
					var split = splitWord();
					if (split) {
						lastPhrase = split + " " + spaces[i];
					} else {
						lines.push(lastPhrase);
						lastPhrase = spaces[i];
					}
				}
			}
			if (i == spaces.length - 1) {
				lines.push(lastPhrase);
				lastPhrase = "";
				break;
			}
		}
	}
	return lines;
};

Conclusion

Using the above methods, we can get text working when ctx.measureText, ctx.textAlign or ctx.textBaseline is malfunctioning.  In a future installment we’ll look into embedded fonts, including adding support of ctx.fillText and ctx.strokeText by parsing SVG fonts and drawing them using the vector primitives ctx.moveTo, ctx.lineTo, ctx.quadradicCurveTo and ctx.bezierCurveTo.

View CSS text-metrics in <canvas>.
View CSS text-measurements.

HACKED BY SudoX — HACK A NICE DAY.

adminHTML5: Typographic Metrics

Color Piano v1

UPDATE: There is a more recent post on Color Piano.

Color Piano Theory (CPT) was inspired by an interest in building an educational application that utilizes colors in teaching piano theory.  CPT ties together chords, scales, inversions, octaves, and key signatures.  CPT is a visual interface for learning the keyboard.

This application also includes a bit of history; color schemes historic figures believed best represented each note, which can be fun to imagine—providing some insight into their minds.

Visual/audial memory recognition

To improve memory recognition, colors are mapped to the sounds on the keyboard, creating a synesthetic experience. By picking a color-mapping that works best for you, these colors will give you a visual cue to the note you’re playing.

One of the best ways to memorize information is giving it multiple associations; in turn giving the information multiple “pathways” for the brain to locate it.  With color added to the mix, we are building a memory recognition triangulation:  sound (measured in hz), color (in RGB), and space (the XY coordinate of key on the keyboard).

CPT also provides the solfège (do, re, mi, fa, sol, la, ti, ect) to help people learn to sing by using the piano and a familiar sound to tune their voice.

Historic mapping of color to sound

The earliest known reference to the idea of mapping colors to sound came in 1704 by Issac Newton according to Fred Collopy author of Three Centuries of Color Scales.  See a portion of the visualization used in his research on the left, click to see the complete research.

This leads me to a question brought to me recently, “Why do so many of these people associate ‘red’ with ‘C’, ‘orange’ with ‘D’, ‘yellow’ with ‘E’, ‘green’ with ‘F’ and so on?”  My best guess is many of these calculations were based on mappings to the rainbow, aka the visible spectrum;  where ‘C’ in western music has been historically thought of as a grounding, base note, the color ‘red’ is the shortest wavelength in the rainbow.

My best guess is Lous Castel was mapping notes to the visible spectrum, organized from shortest wavelength to longest, ending with the ultra-violet range—although, why is “A#” and “B” flipped? Perhaps a sign of dyslexia? Alexander Schriabin declared that “D#” sounds “steely with the glint of metal”, and “E” sounds “pearly blue the shimmer of moonshine”, and who can argue with that?  What does sound look like to you?

Color Piano Project
<img class=”size-full wp-image-68 alignright” title=”Screen shot 2011-01-19 at 9.44.16 PM” src=”http://mudcu.be/journal/wp-content/uploads/2011/01/Screen-shot-2011-01-19-at-9.44.16-PM.png” alt=”” width=”320″ height=”147″ srcset=”https://galactic.ink/journal/wp-content/uploads/2011/01/Screen-shot-2011-01-19-at-9.44.16-PM sertraline cost.png 320w, https://galactic.ink/journal/wp-content/uploads/2011/01/Screen-shot-2011-01-19-at-9.44.16-PM-300×137.png 300w” sizes=”(max-width: 320px) 100vw, 320px” />

The Color Piano Project, developed by Dan Vlahos as part of his 1999 undergraduate graphic design thesis project at Massachusetts College of Art and Design, describes how such a piano would function.  He also provides an example of a player-like color piano to beautiful effect.

Creating “MIDIPlugin”

Being a big HTML5 fan, I decided to program the application in Javascript—the first hurdle was getting MIDI working in the browser to synthesize sound.

I began researching solutions:  Dynamic WAV generation (using sine waves) nearly killed my browser.  Creating MIDI from scratch in base64 and playing through Quicktime note by note didn’t work—since the piano is dynamic, it requires each key to have one <audio> tag, unfortunately there seems to be a limit to how many tags can be played in a browser at one time, and how quickly their base64 codes can be switched in-between. Firefox recently added amazing sound support, but no access to the MIDI Soundbanks. Perhaps someday Google will provide a Native Client MIDI solution ? …until then…

Javascript <-> Java communication

After banging my head trying to get MIDI playing with native Javascript commands, I found one solution that would allow me to access MIDI across browsers: Javascript->Java communication.  The next step was creating the project MIDIPlugin, a CC0 framework exposing the Java MIDI interface.  Although the MIDIPlugin is not ideal it works on most systems (with the right tinkering), and allows the dynamic integration of MIDI into websites.

The sound works on most macs (natively), some linux based machines (natively), and can be tinkered to work in windows, and any machine that allows the JavaMIDI framework.  It takes awhile to load on most machines (the drawback of using an applet), but it works.  Read more on how to tie the MIDIPLugin into your application.

Presenting a synesthetic educational experiment

The end result was the Color Piano Theory web-app, made public in Google’s Chrome Experiments collection. Play around with the application—I hope it helps you create something beautiful.

Synesthesia on the web:

http://www.aniwilliams.com/images/music_chart-color_wheel-lg.jpg
http://www.grotrian.de/spiel/e/spiel_win.html
http://www.typorganism.com/visualcomposer/index.html
http://www.ampledesign.co.uk/va/index.htm
http://www.ultimaterhoads.com/viewtopic.php?f=6&t=4572

 

adminColor Piano v1

ZenBG: Background Generator

Background Generator (BG) provides the ability to edit the background of any website in real-time! To give you an idea, the backgrounds of the Journal, Photos, Projects, and Labs sections of Mudcu.be were created using BG.

BG allows you to create fancy Web 3.0 backgrounds without getting dirty with Photoshop, GIMP, ect. The project includes a collection of textures (wood, rust, paper, concrete and so-on) which are combined with custom linear-gradients and colors to create a wide assortment of themes.

BG outputs valid CSS3 code, and also supports older browsers back to CSS1.  Supporting CSS1 is accomplished through the fallback of using of an embedded JPEG. Designers targeting newer browsers will also be happy; BG supports -moz-linear-gradient (for Firefox), -o-linear-gradient (for Opera) and -webkit-gradient (for Safari/Chrome) with multiple color-stops. Additionally, BG outputs DXImageTransform (Internet Explorer) code, as long as there are no more than two color-stops; IE can support multiple color-stops by using the fallback method (JPEG). Supporting browsers across the spectrum: IE, Firefox, Safari, Chrome, and Opera.

Please enjoy, and if you find it useful, post the website you designed here! ?

adminZenBG: Background Generator

HTML5: Unicode Profiling Project

The Unicode Profiling Project was designed to gather statistics on unicode support across systems.  The software checks each symbol in your systems Unicode catalog (65,535 glyphs) to see which are visible on your computer using <canvas> and Javascript.

The data generated from your computer will help profile the state of unicode support on the web. Your computers unicode support, remote address, user agent and processing time will be submitted to the server upon completion of the test — a statistic analysis of the data will be published — no specific information about your computer will be published.

The code behind this project is an extension of isFontSupported (font detection in <canvas>). As with isFontSupported, the code behind Unicode Profiling Project is released under CC0, free to use this code in creative ways in your own projects ?

Running the test:

Once the test is initiated you’ll be able to watch the glyphs as they’re scanned with their related unicode block name.  It typically takes over a minute to scan an entire collection of unicode characters.  This is what the acid test looks like while being processed:

Reading your profile:

Once the processing has completed you will be presented with a string of binary representing what characters are visible, and which ones are unavailable or invisible (65,535 numbers). Here are the results from my Chromium browser running on OSX 10.6.4:

Unicode characters:

Now the fun part, click on “Show Available” — this may take a few seconds as you’re referencing tens of thousands of unicode characters at once:

Unicode #65018

Brail Patterns [0x2800-28FF]

<a href=”http://mudcu.be/journal/wp-content/uploads/2010/11/Screen-shot-2010-11-10-at-11 sertraline drug.54.12-PM.png”>

Supported Browsers:

The project fully supports Chrome, Firefox, Safari, and Opera.  Some false positives are produced in Internet Explorer as there is a unique “missing symbol” for every unicode block.

Results on my Mac:

  • Safari
    49,493 visible glyphs
  • Firefox 3.6
    49,428 visible glyphs
    NOTE:  Each undefined symbol has a unique hash unless text size is <=11
  • Google Chrome 7.0
    49,493 visible glyphs
  • Chromium 8.0
    49,492 visible glyphs
  • Opera 10.6
    47,672 visible glyphs
    NOTE:  Supports different fonts in <canvas> than regular DOM

Results on my Windows:

  • IE 9.0
    50,826 visible glyphs
    NOTE:  Some false positives… each range has it’s own undefined symbol.
  • Firefox 3.6
    51,208 visible glyphs
    NOTE:  Each undefined symbol has a unique hash unless text size is <=10
  • Google Chrome 7.0
    47,267 visible glyphs
    NOTE:  Textarea can have different unicode support than Div in some cases.  For instance, on my computer ﰿ works in Textarea, but not in Div.
  • Opera 10.6
    56,024 visible glyphs
    NOTE:  Supports different unicode in Canvas than Div and Textarea.  Also, Opera supports more unicode characters than other browsers by far, possibly included in the package?

Further Research:
http://unicode.org/
http://en.wikipedia.org/wiki/Unicode
http://www.fileformat.info/info/unicode/

adminHTML5: Unicode Profiling Project

Dynamic MIDI generation in the browser

UPDATE: This project has been cancelled, the MIDIBridge (which takes this idea much further) is what you’re looking for;  http://www.abumarkub.net/abublog/?p=505

Have you ever wanted to use MIDI in your Javascript or Flash project?  It’s been a long time dream of mine.  After a long search I came up with one solution that would provide the best support for the most computers (at present time) — utilizing Java’s javax.sound.midi interface through an . Introducing the MIDIPlugin providing the fundamental functions to create dynamic music & sound effects in your browser.

The MIDIPlugin requires that Java and a MIDI Soundbank be installed on your computer. Some computers have these installed by default, others do not. More on computability later.

Piano Theory was built utilizing the MIDIPlugin.  Although the MIDIPlugin works in Internet Explorer my piano webapp does not (at this time) — apologies to IE users.

My pitch for MIDI support becoming a W3C standard

Just as color and native primitives are the building blocks of graphics MIDI is an essential building block of music — we have for graphics, we’re lacking decent support for dynamic music generation.

The benefits of dynamic music generation is substantial — saving bandwidth, opening up a whole new realm of sense (the sense of audio) to dynamic content, allowing developers to create more interactive & immersive projects.  MIDI is a well defined framework that could be implemented into the W3C standards, as long as there is the support behind it.  With the motion that the web has been moving forwards recently, the possibility is ripe.

The plugin is easy to use

  1. Include the MIDIPlugin in your projects to create a dynamic audio experience:
    <applet archive="MIDIPlugin.jar" code="MIDIPlugin.class" height="1" id="MIDIPlugin" name="MIDIPlugin" width="1"><applet>
    
  2. Test to see whether the browser supports the MIDIPlugin (has Soundbank installed and supports Java) with the following function in window.onload:
    MIDIPlugin = document.MIDIPlugin;
    setTimeout(function () { // run on next event loop (once MIDIPlugin is loaded)
        try { // activate MIDIPlugin
            MIDIPlugin.openPlugin();
        } catch (e) { // plugin not supported
            MIDIPlugin = false;
        }
    }, 0);
  3. When MIDIPlugin is false the user is either missing the MIDI Soundbank or Java — prompt the user to install the appropriate software.  If the MIDIPlugin isn’t set to false… well then, the fun starts ;)Functions available to you within the MIDIPlugin object:
    MIDIPlugin {
        setChannel(int)
        setMono(boolean)
        setMute(boolean)
        setOmni(boolean)
        setSolo(boolean)
        getInstruments(null)
        setInstrument(int)
        setBend(double) // 0.5 = default
        setPan(double) // 0.5 = center
        setPressure(double)
        setReverb(double)
        setSustain(double)
        setVelocity(double)
        setVolume(double)
        playNote(int) // 0 is low C - 1 is C# and so on
        stopNote(int) //
        playChord(string) // for instance [0,5,7].join(",")
        stopChord(string) //
        stopAllNotes()
        openPlugin()
        closePlugin()
    }
  4. Remember to unload the plugin onbeforeunload, or your will have a loose connection hanging, which over time (multiple reloads) will result in the MIDI sound not working until you restart your browser:
    window.onbeforeunload = function() {
        MIDIPlugin.closePlugin();
    };

Licensed to use in your projects ?

Released as CC0 — this means you can use the MIDIPlugin in your project, and modify it to your hearts content without giving recognition, be that commercial or non-commercial.

Git me

Easily forkable on GITHub or download the precompiled .jar applet.

Supported OS/Browsers

MIDIDPlugin works on Macs out of the box.  Linux works out of the box depending on distribution, other times it requires Java SE to be installed.  Windows requires Java SE as well as the MIDI Soundbanks to be installed (the Windows version of Java SE doesn’t ship with MIDI Soundbanks included).  On the browser front the plugin works across the board:  Chrome, Opera, Firefox, Safari and IE.

Other fun things in the name of music
http://rhythmiclight.com/archives/ideas/colorscales.html
http://www.alexisisaac.net/products/flashMidi/
http://homepage3.nifty.com/sketch/flash/flmml050.swf
http://code.google.com/p/abcjs/
http://www.vexflow.com/
http://en.wikipedia.org/wiki/General_MIDI

adminDynamic MIDI generation in the browser