Weston Ruter

Web application developer in Portland, Oregon

Portland, Oregon Snow Driving

I took this video outside of Shepherd Interactive, my work. View in HD to see the sparks fly :-) See also the footage of my commute home which ended up being a journey half on foot from my work to downtown: part 1 and part 2.

What follows are screengrabs from Google Maps of the traffic around 5pm. One word: gridlock.

HTML5 Audio Read-Along

Jump to demo below to listen while reading along to the nativity story from the Gospel of Luke.

I guess I’ve been on an HTML5 Audio kick lately, with this and my previous post on using it to play Google’s Text-To-Speech (TTS) service on Google Translate. I wrote that post while attempting to build a text-to-speech interface which audibly reads the text on the page while highlighting the currently-spoken word (like my old favorite ReadPlease), but a bug in Google Chrome for Mac discouraged me from finishing it. However, when I saw the inestimable Paul Irish‘s post about the jquery-singalong plugin which times HTML5 Audio with text and a bouncing ball cue, I was inspired to do the same but for a (non-TTS) read-along.

ESVThe text I chose was the nativity story from the Gospel of Luke in the English Standard Version (ESV), not only in keeping with the (true) Christmas spirit but also because the ESV Online has an excellent API which allows both a passage’s text and its audio to be queried. With the text and audio in hand, each of the words in the text needs to be time-indexed for its begin time and duration in the corresponding audio. In the past, audio Bibles were divided into chapter segments only and that was as granular as you could go; the ESV team did the innovation of taking this granularity down to the verse-level. Unfortunately, however, the granularity is not available at the word-level. Therefore, in order to make this exciting (IMHO) read-along demo work, I manually traversed the audio to find each word’s begin time and duration (spending more time than I care to say), and I added these time indicies to the word markup as data-begin and data-dur attributes, akin to SMIL’s begin and dur attributes (which I suppose I could use). I also spruced up the markup for the passage by incorporating some HTML5 constructs as well as adding OSIS verse elements as I’ve been talking about over at Open Scriptures.

The read-along demo has been tested in Firefox 3.5, Chrome 4, and Safari 4 (as expected, it will not work in Internet Explorer). Chrome plays the MP3 as served from the ESV API. Firefox doesn’t support MP3 so I include an OGG Vorbis source as well. Safari seems to have trouble playing the MP3 and can’t play OGG, so I also include an 8kHz WAV source as well.

What it does: Upon playing the audio, the word corresponding to the one currently being spoken is highlighted (via DOM Range/Selection); this eliminates the need to re-find your place if you momentarily look away. Likewise, when manually adjusting the seek position, the words which correspond to each position sought will be highlighted; and conversely, clicking a word causes the audio to seek to its corresponding position (and double-clicking will then cause it to start playing). Thus the text itself serves as an interface for navigating the audio. See the inline JavaScript source code for all the magic.

Without further adieu, please enjoy listening while reading along to the Christmas story!


Luke 2:1-20 (ESV)

In those days a decree went out from Caesar Augustus that all the world should be registered. This was the first registration when Quirinius was governor of Syria. And all went to be registered, each to his own town. And Joseph also went up from Galilee, from the town of Nazareth, to Judea, to the city of David, which is called Bethlehem, because he was of the house and lineage of David, to be registered with Mary, his betrothed, who was with child. And while they were there, the time came for her to give birth. And she gave birth to her firstborn son and wrapped him in swaddling cloths and laid him in a manger, because there was no place for them in the inn.

And in the same region there were shepherds out in the field, keeping watch over their flock by night. And an angel of the Lord appeared to them, and the glory of the Lord shone around them, and they were filled with fear. And the angel said to them, Fear not, for behold, I bring you good news of great joy that will be for all the people. For unto you is born this day in the city of David a Savior, who is Christ the Lord. And this will be a sign for you: you will find a baby wrapped in swaddling cloths and lying in a manger. And suddenly there was with the angel a multitude of the heavenly host praising God and saying,

Glory to God in the highest,
and on earth peace among those with whom he is pleased!

When the angels went away from them into heaven, the shepherds said to one another, Let us go over to Bethlehem and see this thing that has happened, which the Lord has made known to us. And they went with haste and found Mary and Joseph, and the baby lying in a manger. And when they saw it, they made known the saying that had been told them concerning this child. And all who heard it wondered at what the shepherds told them. But Mary treasured up all these things, pondering them in her heart. And the shepherds returned, glorifying and praising God for all they had heard and seen, as it had been told them.


Scripture taken from The Holy Bible, English Standard Version. Copyright ©2001 by Crossway Bibles, a publishing ministry of Good News Publishers. Used by permission. All rights reserved. Text provided by the Crossway Bibles Web Service.

Google Text-To-Speech (TTS)

Update : Andufo shared the happy news that more languages are now available in the Google TTS service! I have added a new language selection drop-down for English, Spanish, French, German, Italian, and Hatian Creole.

Google Translate announced the ability to hear translations into English spoken via text-to-speech (TTS). Looking at the Firebug Net panel for where this TTS data was coming from, I saw that the speech audio is in MP3 format and is queried via a simple HTTP GET (REST) request: http://translate.google.com/translate_tts?tl=en&q=text. Google Translate notes that the speech is only available for short translations to English Now multiple languages are supported, and it turns out that the TTS web service is restricting the text to 100 characters. Another restriction is that the service returns 404 Not Found if the request includes a Referer header (presumably one that is not for translate.google.com).

In spite of the limitations of the web service which certainly reflect the intention that the web service is only to be used by Google Translate, thanks to the new HTML5's Audio element and rel="noreferrer", the service may be utilized by client-side web applications like following (Google Chrome 4 recommended):

Google Text-To-Speech (TTS)

I am really excited at the prospect of text-to-speech being made available on the Web! It's just too bad that fetching MP3s on an remote web service is the only standard way of doing so currently; modern operating systems all have TTS capabilities, so it's a shame that web apps and can't utilize them via client-side scripting. I posted to the WHATWG mailing list about such a Text-To-Speech (TTS) Web API for JavaScript, and I was directed to a recent thread about a Web API for speech recognition and synthesis.

Perhaps there is some momentum building here? Having TTS available in the browser would boost accessibility for the seeing-impaired and improve usability for people on-the-go. TTS is just another technology that has traditionally been relegated to desktop applications, but as the Open Web advances as the preferred platform for application development, it is an essential service to make available (as with Geolocation API, Device API, etc.). And besides, I want to build TTS applications and my motto is: If it can't be done on the Open Web, it's not worth doing at all!

Browser Detection Fail

Screenshot of epic browser detection fail: being prompted to install Google Chrome when visiting Google.com while using Chrome

Being prompted on the Google home page to “Install Google Chrome” for “A faster way to browse the web” while I am already using Google Chrome for Mac: epic browser detection fail.

Anyway, Chrome definitely is a faster way to browse the Web. It’s amazing!

CSS Gradients via Canvas

In a current project at Shepherd Interactive, certain page elements were designed with background gradients. Given the desire to minimize the need for externally-loaded background images wherever possible, I thought this would be a great opportunity to play around with WebKit's proposed CSS Gradients, which are natively supported by Safari, Chrome, and other WebKit-based browsers. In being a WebKit proposal, however, CSS Gradients are not (yet) natively supported in other rendering engines as used by Firefox, Opera, and Internet Explorer.

CSS Gradients via Canvas provides a subset of WebKit's CSS Gradients proposal for browsers that implement the HTML5 canvas element. To use, just include css-gradients-via-canvas.js (12KB) anywhere on the page (see examples below). Unlike WebKit, this implementation does not currently allow gradients to be used for border images, list bullets, or generated content. The script employs document.querySelectorAll()—it has no external dependencies if this function is implemented; otherwise, it looks for the presence of jQuery, Prototype, or Sizzle to provide selector-querying functionality.

The implementation works in Firefox 2/3+ and Opera 9.64 (at least). Safari and Chrome have native support for CSS Gradients since they use WebKit, as already mentioned. Beginning with version 3.6, CSS Gradients are also natively supported by Firefox and this implementation will defer in such case; note that you will need to specify two separate background CSS properties, one with -webkit-gradient and another with -moz-linear/radial-gradient which has a different syntax). This implementation does not work in Internet Explorer since IE does not support Canvas, although IE8 does support the data: URI scheme, which is a prerequisite (see support detection method). When/if Gears's Canvas API fully implements the HTML5 canvas specification, then this implementation should be tweakable to work in IE8. In the mean time, rudimentary gradients may be achieved in IE by means of its non-standard gradient filter.

CSS Gradients via Canvas works by parsing all stylesheets upon page load (DOMContentLoaded), and searches for all instances of CSS gradients being used as background images. The source code for the external stylesheets is loaded via XMLHttpRequest—ensure that they are cached by serving them with a far-future Expires header to avoid extra HTTP traffic. The CSS selector associated with the gradient background image property is used to query all elements on the page; for each of the selected elements, a canvas is created of the same size as the element's dimensions, and the specified gradients are drawn onto that canvas. Thereafter, the gradient image is retrieved via canvas.toDataURL() and this data is supplied as the background-image for the element.

A few notes regarding interactivity with this implementation: CSS gradients will not be applied to elements dynamically added after DOMContentLoaded. Additionally, each element that has a CSS gradient applied to it gets assigned a method called refreshCSSGradient(); at any time, this method may be invoked to redraw the gradient on a given element. This is especially useful (and necessary) when an element's size dynamically changes, for example as the result of some user interaction. Likewise, it is important to note that it will not work to rely on event handlers to invoke refreshCSSGradient() on elements whose style is changed by CSS rules with pseudo-selectors like :hover and :active; this is because event handlers are fired before the rule's style changes are applied to the element. Toggling an element's class name by scripting is how you can assure that its style will be changed before calling refreshCSSGradient(). The last example below demonstrates this.

Download and fork the code on GitHub: http://github.com/westonruter/css-gradients-via-canvas

Changelog

Updated license to be GPL/MIT dual license instead of just GPL.
1.3 ():
Detecting native support in Firefox 3.6; it had only been detecting support for 3.6 alpha, which had significantly different syntax. I ported the linear gradient examples over to use the new native Firefox syntax, but am still working on the radial gradients; the syntax has changed a lot!
1.2 ():
Phong Nguyen raised an excellent point in that stylesheets which don't contain any CSS Gradients should be ignored in order to improve performance (in his case, for example, the jQuery UI stylesheets are large and don't need to be parsed). Now you can add class="no-css-gradients" to any style or link element and that will prevent this script from looking for CSS Gradients to apply with canvas.
1.1 ():
Now if cssGradientsViaCanvas.useCache is set to true, the CSS rules containing gradients are cached in sessionStorage instead of having to be re-parsed out of the stylesheets each time a page loads. For this to work, there must be implementations of JSON.stringify() and JSON.parse() available (e.g. json2.js).
Ability to use data: URIs for images is not explicitly detected since testing for the presence of canvas.toDataURI() is sufficient.
1.0.3 ():
Detecting support for native support for CSS Gradients in Firefox 3.6
1.0.2 ():
Now requiring that gradient(…) only be used with the background-image property instead of with the background shorthand properties since the additional background-* properties are not parsed out.

Examples

The following two boxes are from WebKit's Background Gradients Example (a couple browser-specific background properties are disabled in the first one):

Note the following WebKit radial gradient example is very dificult to reproduce using Firefox's syntax. Special thanks to Mark McLaren for writing a close fascimile which appears below:

Here is a bonus diagonal rainbow…but wait, there's more! Clicking on the element causes its width to increase and when it does, its gradient is adjusted (see notes above).