Overview

When applying effects to text on the web, designers have traditionally been constrained to those provided by CSS. In the majority of cases this is entirely suitable – text is text right? Yet still, there exist numerous examples of designers combining CSS properties or gifs and images to create effects that evoke something more playful. Precisely here, Blotter exists to provide an alternative.

GLSL Backed Text Effects with Ease

Blotter provides a simple interface for building and manipulating text effects that utilize GLSL shaders without requiring that the designer write GLSL. Blotter has a growing library of configurable effects while also providing ways for student or experienced GLSL programmers to quickly bootstrap new ones.

Atlasing Effects in a Single WebGL Back Buffer

Blotter renders all texts in a single WebGL context and limits the number of draw calls it makes by using atlases. When multiple texts share the same effect they are mapped into a single texture and rendered together. The resulting image data is then output to individual 2d contexts for each element.

Animation Loop

Rather than executing on a time based interval, Blotter's internal animation loop uses requestAnimationFrame to match the browser's display refresh rate and pause when the user navigates to other browser tabs; improving performance and preserving the battery life on the user's device.

What Blotter Isn't

Any texts you pass to Blotter can be individually configured using familiar style properties. You can use custom font faces through the @font-face spec. However, Blotter ultimately renders the texts passed to it into canvas elements. This means rendered text won't be selectable. Blotter is great for elements like titles, headings, and texts used for graphic purposes. It's not recommended that Blotter be used for lengthy bodies of text.

The Basics

To get started, download Blotter and include it in the head of the HTML document like so: <script src="./path/to/blotter.js"></script>. Once you've done this, visit the page in your browser, open the console, and type “Blotter”. If a function is returned, you're ready to begin.

Plain Text

Before adding any effects, you should learn how to replace text in your document with plain text rendered1 by Blotter. Instances of Blotter take at least one Text object and a single Material object, which is the source of any Blotter effect you wish to apply to your texts.

In the following example, we create a single Text object, apply the basic Blotter.Material material (which simply draws the Text as described by its properties) and add the rendered canvas to an element on the page.

Texts rendered through Blotter are inserted into the DOM as canvas elements.

JavaScriptHTML

Ready-made Effects

Blotter is a tool designed to allow anyone, regardless of their knowledge of GLSL itself, to use the power of GLSL fragment shaders2 to create and apply real-time visual effects to texts on the web. To this end, Blotter provides a growing collection of ready-to-use and configurable materials.

If you're unfamiliar with GLSL and fragment shaders, check out Shadertoy and read this Stackoverflow thread to learn about pixel shaders and what they are capable of.

The configurability of provided materials will depend on the material itself, but the basic interface will typically center around the manipulation of “uniforms”. Think of uniforms as values that describe how effects are rendered. For example, a material that bends and distorts your text as if viewed on the bottom of a pool might have a uniform called uSpeed to help you control the speed at which the distortion occurs when animated.

Below, we will use Blotter's ready-made LiquidDistortMaterial to illustrate how to use Blotter materials by controlling uniforms.

JavaScriptHTML

Materials

Blotter Materials describe how your texts will be rendered and provide the interface through which you can introduce variety in the characteristics of any effect.

Listed below are Blotter's current set of ready-to-use materials. For information on creating custom materials for private use, or for contributing new materials to Blotter to be listed here, see the documentation.

RollDistortMaterial

Class for creating a rolling distortion effect using Blotter.js. It inherits from Blotter.Material, and therefore has the same properties and functions as Blotter.Material.

constructionvar material = new Blotter.RollDistortMaterial();

uniformsmaterial.uniforms

An object that holds the uniforms that describe how the material's effects should be rendered.

uTimematerial.uniforms.uTime

Represents how much time has elapsed since the last frame in milliseconds.

valuematerial.uniforms.uTime.value

A floating point number representing how much time has elapsed since the last frame in milliseconds. Default value is 0.0.

typematerial.uniforms.uTime.type

The uniform type of the value being passed to the shader. This value is a static "1f".

uDistortionmaterial.uniforms.uDistortion

Represents the predominant distortion to apply to texts.

valuematerial.uniforms.uDistortion.value

A floating point number representing the predominant distortion to apply to texts. Default value is 0.0.

typematerial.uniforms.uDistortion.type

The uniform type of the value being passed to the shader. This value is a static "1f".

Documentation

What follows are the main classes an average developer will encounter when working with Blotter. Many classes and utilities are left out of this documentation, as they are more internal to the codebase rather than part of the interface one would need to use Blotter as it comes. If you would like to see Blotter laid bare, please view the source.

Blotter

Blotter is the window level interface for interacting with all Blotter classes and properties. Whenever you want to apply a new Blotter material to texts on your page, you will instantiate a new object of the Blotter class.

constructionvar blotter = new Blotter(material[, options]);

Create a new instance of Blotter where material is an instance of any Blotter.Material and options is an optional JavaScript object with any of the following optional parameters.

textsoptions.texts

An array of Blotter.Text objects, or optionally a single Blotter.Text object. Any texts given to an instance of Blotter will be drawn according to the instance's material.

ratiooptions.ratio

The backing store size in relation to the canvas element, or otherwise known as the devicePixelRatio. This property essentially tells Blotter what pixel ratio to draw texts for in relation to the screen, and will effect the clarity of rendered texts on retina devices. Blotter determines this property by default to ensure the most appropriate pixel ratio for your user's device, so it's unlikely you'll need to set this yourself.

autobuildoptions.autobuild

Tells the instance of Blotter whether or not to immediately build and configure itself using the supplied Blotter.Material instance and optional parameters such as supplied Blotter.Text objects. By default, Blotter will assume this to be true. If for some reason you intend to add additonal texts or change the material prior to use, it would be a good idea to set this to false and later set the value of needsUpdate to true in order to trigger a fresh build.

autostartoptions.autostart

Tells the instance of Blotter whether or not to immediately begin rendering and updating universal uniforms. By default, Blotter will assume this to be true.

autoplayoptions.autoplay

Tells the instance of Blotter whether or not to immediately begin the render loops for all Blotter.RenderScope objects created for each of its texts. By default, Blotter will assume this to be true.

Each draw will happen on requestAnimationFrame.

Listen for the ready event to perform work after the instance has finished building itself.

needsUpdateblotter.needsUpdate = true

Occasionally you may need to perform actions on your instances of Blotter that require it to repeat important preparatory steps necessary for rendering the instance's texts. To do so, simply set the value of blotter.needsUpdate to true and the instance will take care of the rest. You can listen for the instance's update event if you need to do any work after the update has completed.

materialblotter.material

A reference to the Blotter.Material instance being used to render your texts. Setting this property after your Blotter object's initialization requires that your Blotter instance be rebuilt by setting the value of its needsUpdate property to true.

textsblotter.texts

A reference to the Blotter.Text objects being rendered by your instance of Blotter. Setting this property after your Blotter object's initialization requires that your Blotter instance be rebuilt by setting the value of its needsUpdate property to true.

startblotter.start()

Tells the instance of Blotter to begin rendering and updating universal uniforms.

At the time of each draw on requestAnimationFrame the instance will emit a render event.

stopblotter.stop()

Tells the instance of Blotter to stop rendering and updating universal uniforms.

setMaterialblotter.setMaterial(material)

Sets the Blotter.Material instance being used to render your texts. In order for this change to go into effect you'll have to rebuild your Blotter instance by setting the value of its needsUpdate property to true.

addTextblotter.addText(text)

Alias method for blotter.addTexts(text), see below.

addTextsblotter.addTexts(texts)

Adds one or more Blotter.Text objects to be rendered by your instance of Blotter. In order for this change to go into effect you'll have to rebuild your Blotter instance by setting the value of its needsUpdate property to true.

removeTextblotter.removeText(text)

Alias method for blotter.removeTexts(text), see below.

removeTextsblotter.removeTexts(texts)

Removes one or more Blotter.Text objects from being rendered by your instance of Blotter. In order for this change to go into effect you'll have to rebuild your Blotter instance by setting the value of its needsUpdate property to true.

forTextblotter.forText(text)

Returns an instance of Blotter.RenderScope for the given Blotter.Text object.

boundsForTextblotter.boundsForText(text)

Returns a JavaScript object containing the width (w), height (h), x-offset (x), and y-offset (y) of the given Blotter.Text object in the backbuffer the Blotter instance uses to render the material's effect collectively for all texts in the instance. These values, especially the x-offset and y-offset, will likely be of little use to you.

Blotter.Material

Blotter Materials describe how your texts will be rendered and provide the interface through which you can control uniforms to introduce variety in the characteristics of any effect.

As the Blotter.Material class is the abstract base class for all Blotter Materials, using this class will simply render your texts according to their font and style properties. See the documentation for specific Materials for further details on their individual usages.

constructionvar material = new Blotter.Material();

Create a new instance of Blotter.Material.

needsUpdatematerial.needsUpdate = true

Rarely, you may need to perform actions on your instances of Blotter.Material that require it to tell the instance of Blotter to repeat important preparatory steps necessary for rendering the instance's texts. To do so, set the value of material.needsUpdate to true and the instance will take care of the rest. You can listen for the Blotter instance's update event if you need to do any work after the update has completed.

mainImagematerial.mainImage

For every Blotter Material, there is an underlying GLSL fragment shader that describes how the individual Material's effect will be rendered. The material.mainImage property returns the shader's GLSL in string form. Setting this property after your Blotter.Material object's initialization requires that your Blotter.Material instance be rebuilt by setting the value of its needsUpdate property to true.

uniformsmaterial.uniforms

The fundamental interface for all effects in Blotter. Every Material will provide its own array of uniform objects, each having a type and a value. To manipulate any effect, simply set the value of the uniform you wish to change.

Listed below are the four uniform types available in Blotter.

"1f"

For "1f" type uniforms, values should be set using floating point values.material.uniforms.myUniform1.value = 0.5;.

"2f"

For "2f" type uniforms, values should be set using an array of two floating point values.material.uniforms.myUniform2.value = [0.5, 0.5];.

"3f"

For "3f" type uniforms, values should be set using an array of three floating point values.material.uniforms.myUniform3.value = [0.5, 0.5, 0.5];.

"4f"

For "4f" type uniforms, values should be set using an array of four floating point values.material.uniforms.myUniform4.value = [0.5, 0.5, 0.5, 0.5];.

As stated, every Blotter Material will provide its own uniforms unique to the characteristics of its own effect. However, there are a handful of uniforms on which all Blotter Materials rely. These are listed below.

uResolution

The resolution of an individual text within the mapping material being rendered by your material. Type "2f". You should never set the value of this uniform yourself.

uGlobalTime

The global time in seconds. Type "1f". You should never set the value of this uniform yourself.

uTimeDelta

The render time in seconds. Type "1f". You should never set the value of this uniform yourself.

uBlendColor

The base color against which all blending should occur within an effect. Type "4f". The default value for this uniform is white, or [1.0, 1.0, 1.0, 1.0], where each index in the array represent an R,G,B, and A value respectively in a 0.0 to 1.0 range.

This uniform is important for effects that sample the area around your texts for blending purposes, such as for the RGB splitting that occurs in the ChannelSplitMaterial, and you should set it to match the RGBA color that will be the background for any of your texts.

uPixelRatio

The pixel ratio of the user's device. Type "1f". You should never set the value of this uniform yourself.

Blotter.Text

For each string of text you wish to render with a given Material, you should create an instance of Blotter.Text.

constructionvar text = new Blotter.Text(value[, properties]);

Create a new instance of Blotter.Text where value is string of text1 and properties is an optional styles object for applying styles to your texts prior to them being rendered with your Material. The styles object takes any of the following optional parameters.

familyproperties.family

A string representing the font family to be applied to the text. The default value is 'sans-serif'.

sizeproperties.size

A number representing the size of the rendered text. The default value is 12.

leadingproperties.leading

A number representing the leading or line-height of the rendered text. The default value is 1.5. You should try to keep this number above 1.0, as Blotter bases text positions within the text atlas on computed text heights, and leading values that are lower than the text size they coorespond with can cause texts to overflow into adjacent canvases.

fillproperties.fill

A string representing the color of the rendered text. The default value is '#000'.

styleproperties.style

A string representing the style of the rendered text. The options are 'normal', 'italic', or 'bold'. The default value is 'normal'.

weightproperties.weight

A number representing the weight of the rendered text. The default value is 400.

paddingproperties.padding

A number representing the padding around the rendered text. The default value is 0.

paddingTopproperties.paddingTop

A number representing the padding above the rendered text. The default value is 0.

paddingRightproperties.paddingRight

A number representing the padding to the right of the rendered text. The default value is 0.

paddingBottomproperties.paddingBottom

A number representing the padding below the rendered text. The default value is 0.

paddingLeftproperties.paddingLeft

A number representing the padding to the left of the rendered text. The default value is 0.

Remember, Blotter is meant for small strings of text such as single words, titles, or texts used for graphics purposes. You should not create Blotter.Text instances for whole sentences or longer strings of text.

needsUpdatetext.needsUpdate = true

Rarely, you may need to perform actions on your instances of Blotter.Text that require it to tell the instance of Blotter to repeat important preparatory steps necessary for rendering the instance's texts. To do so, set the value of text.needsUpdate to true and the instance will take care of the rest. You can listen for the Blotter instance's update event if you need to do any work after the update has completed.

valuetext.value

The string of text for the instance. Setting this property after your Blotter.Text object's initialization requires that your Blotter.Text instance be rebuilt by setting the value of its needsUpdate property to true.

propertiestext.properties

The styles object representing the style properties being applied to your string of text. Setting this property after your Blotter.Text object's initialization requires that your Blotter.Text instance be rebuilt by setting the value of its needsUpdate property to true.

Blotter.RenderScope

Blotter RenderScopes are the primary interface for interacting with your texts on an individual basis. When you render one or more texts inside an instance of Blotter, the instance will create the scopes for you automatically, and you can use them to perform a number of critical actions, such as placing your rendered texts within DOM elements and setting uniform values specific to individual texts while rendering texts collectively in a single material.

obtainmentvar scope = blotter.forText(text);

You will never create Blotter.RenderScope objects directly. Instead, you should obtain them from your instance of blotter by calling blotter.forText(text) where text is an instance of Blotter.Text known to your blotter object.

textscope.text

The instance of Blotter.Text for the scope.

playingscope.playing

A boolean value indicating whether or not the scope will actively be rendered by the Blotter instance. On initialization, this value is set to the value of the Blotter instance's autoplay property.

timeDeltascope.timeDelta

The scope's render time in seconds.

lastDrawTimescope.lastDrawTime

The scope's last draw time.

frameCountscope.frameCount

The number of times the scope has been rendered.

domElementscope.domElement

The DOM element for the given scope. This will return a HiDPI canvas with a 2d context containing an up-to-date rendering of your text. You should place this element where you want the text for the scope to appear on the page. You may access the element directly through this property, or append it to another DOM element using the convenience method, scope.appendTo(domElement).

Because it is likely you will want to respond to pointer events on these elements, Blotter RenderScopes emit the "mousedown", "mouseup", "mousemove", "mouseenter", and "mouseleave" events.

contextscope.context

The 2d context for the scope's domElement.

materialscope.material

The primary interface for interacting with the Material being rendered by your Blotter instance in a manner specific to an individual text. This is critically important for updating uniforms in a non-global way. For example, if you are rendering several texts using a single Material, and want to have one of those texts update its visual appearance on a mouseover event without changing the other texts, you would want to access the uniforms for the Material through the scope, and not on the Material's instance directly.material.uniforms.myUniform.value = 0.5; // GLOBAL TO ALL TEXTSscope.material.uniforms.myUniform.value = 0.5; // TEXT SPECIFIC

playscope.play()

Tells the scope to begin or resume being rendered by the instance of Blotter.

pausescope.pause()

Tells the scope to pause being rendered by the instance of Blotter.

renderscope.render()

Tells the scope to update its rendering one time.

appendToscope.appendTo(domElement)

A convenience method for appending the scope's domElement to the page.