A JavaScript API for drawing unconventional text effects on the web.


Blotter logo

A JavaScript API for drawing unconventional text effects on the web.



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, and should in most cases be applied to words individually.


Download the minified version.

To apply text effects, you'll also want to include at least one material, so download one of Blotter's ready-made effects, such as the ChannelSplitMaterial.

Include both in your HTML.

<script src="path/to/blotter.min.js"></script>
<script src="path/to/channelSplitMaterial.js"></script>

The following illustrates how to render Blotter's ChannelSplitMaterial in the body of your page with default settings.

<!doctype html>
    <script src="path/to/blotter.min.js"></script>
    <script src="path/to/channelSplitMaterial.js"></script>
      var text = new Blotter.Text("Hello", {
        family : "serif",
        size : 120,
        fill : "#171717"

      var material = new Blotter.ChannelSplitMaterial();

      var blotter = new Blotter(material, { texts : text });

      var scope = blotter.forText(text);


Making Changes / Custom Builds

Firstly, install Blotter's build dependencies (OSX):

$ cd ~/path/to/blotter
$ npm install

The blotter.js and blotter.min.js files are built from source files in the /src directory. Do not edit these built files directly. Instead, edit the source files within the /src directory and then run the following to build the generated files:

$ npm run build

You will the updated build files at /build/blotter.js and /build/blotter.min.js.

Without Three.js / Without Underscore.js

Blotter.js requires Three.js and Underscore.js. If you're already including these files in your project, you should remove them from the defFiles array in the Gruntfile and re-run the build script.

Note: In order to decrease the total build size, Blotter uses a custom build of Three.js that only includes modules Blotter.js relies on. For more information view the build-custom-three script in package.json.

Custom Materials

The documentation for creating custom materials can be found in the Wiki.


Blotter is not possible without these contributions to JavaScript.

Some projects and people who have helped inspire along the way.

  • Two.js
    Jono Brandel's Two.js has provided much inspiration for Blotter's documentation and API design.
  • Reza Ali
    Reza's Fragment was a fundamental part of the development process for writing Blotter's Fragment shaders, and Reza kindly allowed Blotter to include an array of shader helper functions from Fragment.
  • Mitch Paone
    I was introduced to Mitch's work in computational typography while working on Blotter, and the work Mitch has done with DIA has been hugely motivational.
  • Stan Haanappel
    Stan Haanappel is a designer whose work with type has been inspirational to Blotter.
  • The Book of Shaders
    The Book of Shaders by Patricio Gonzalez Vivo and Jen Lowe is where anyone looking to learn more about writing shaders should begin.
  • Shadertoy
    Shadertoy has been a critical part of my personal learning experience while working on Blotter.

✌️ - Bradley Griffith

  • Uncaught DOMException: Failed to construct 'ImageData': The input data length is not a multiple of 4.

    Uncaught DOMException: Failed to construct 'ImageData': The input data length is not a multiple of 4.

    As far as I can tell, this error appears in every implementation I've seen of blotter, except for blotter's website.

    Following the instructions given under the 'Basics', this error appears, and it seems to be preventing any rendering at all of Blotter.

    Examples of this error occurring, from looking at codepen:




    opened by brianbrennan 14
  • Blotter Text not working correctly, is anything outdated?

    Blotter Text not working correctly, is anything outdated?

    Im trying to render a text with a "RollingDistortMaterial" material.

    In my computer it works correctly, without any issues or warnings: image

    This is the code:

        let fontContainer = document.querySelector("#blotter-text")
        let fullText = fontContainer.innerText.toUpperCase()
        fontContainer.innerText = "";
        fullText = fullText.split(" ")
        fullText.forEach(function (e) {
        function convertToBlotter(word) {
            let text = new Blotter.Text(word, {
                family: "Media Sans Extended",
                size: 66.5,
                needsUpdate: true,
                fill: "#FFFFFF",
                leading: "1",
                paddingLeft: 10,
                paddingRight: 10
            let material = new Blotter.RollingDistortMaterial();
            let blotter = new Blotter(material, { 
                texts: text 
            blotter.needsUpdate = true;
            let scope = blotter.forText(text);

    But in other computers it gets this errors and warnings, am i doing anything wrong? is anything outdated?


    opened by wwwisie 10
  • Add more CSS properties

    Add more CSS properties

    I am looking into using more CSS properties like:

    var text = new Blotter.Text("observation", {
      vertical-align: middle,
      mix-blend-mode: multiply

    Did not find anything in the docs to add custom props. Any plans to add this feature?

    opened by duezerouno 4
  • Responsive font size

    Responsive font size


    I am trying to make my blotter.js text responsive, using something like the below CSS, that ordinarily works:

    font-size: calc(30px + 56 * ((100vw - 420px) / 860));

    Because the size is declared in Javascript (currently set to 94), I haven't been able to apply the above.

    I'm hoping for something like this:

    var text = new Blotter.Text("Hello", {
            family : "serif",
            size :"calc(30px + 56 * ((100vw - 420px) / 860))",
            fill : "#171717"

    Please can you advise?

    Thank you!

    opened by elliottmangham 3
  • Custom Font

    Custom Font

    Hey there, thanks for this great library. Quick question, I want to load a custom font from typekit. How might I add that to this example: https://codepen.io/SimonEvans/pen/PQKgyQ

    Also how can I make sure it scales responsively? Thanks for your help!

    opened by adamperlis 3
  • Blotter has build issues on Firefox Nightly (FF60) & Firefox Developer (FF59)

    Blotter has build issues on Firefox Nightly (FF60) & Firefox Developer (FF59)

    The bug is a little intermittent and needs some investigating. On the most recent versions of Firefox it seems that if you load into http://blotter.js.org with a fresh cache the page will mostly not load Blotter elements on the first load. A refresh seems to fix it. Additionally if you load up a material page (https://blotter.js.org/#/materials/FliesMaterial) first, with a fresh cache, Blotter seems to work fine and you can navigate to the homepage with everything working as usual.

    Please investigate.

    Reference comment on Designer News

    opened by bradley 3
  • Animation resets when text changes

    Animation resets when text changes

    I'm using LiquidDistortMaterial for a text, and whenever I change the content, the animation restarts making it very unpleasent. Is there any workaround to avoid this reset? Maybe an offset animation start parameter?

    This is my normal text changing code blotterText.value = text; blotterText.needsUpdate = true;

    opened by gonzam88 2
  • multiline text, or copies of the same line of text

    multiline text, or copies of the same line of text

    Hi - I want to achieve this effect:

    Screen Shot 2019-09-25 at 5 43 48 PM

    where I have multiple copies of the same line of text (or copies of the canvas, etc). I can get blotter to work on a single line of text, but I've tried numerous methods of creating a copy and non of them work.

    For example:

    <div id="distortion-text1" class="progress__content"></div>
    <div id="distortion-text2" class="progress__content"></div>
    <div id="distortion-text3" class="progress__content"></div>


    var elem1 = document.getElementById('distortion-text1')
    var elem2 = document.getElementById('distortion-text2')
    var elem3 = document.getElementById('distortion-text3')
    var scope1 = blotter.forText(text);
    var scope2 = blotter.forText(text);
    var scope3 = blotter.forText(text);

    I've also just tried cloning the canvas and that doesn't work. Blotter seems to just append one single canvas to the last dom node (distortion-text3), no matter what I do.

    opened by heaversm 2
  • Documentation for Custom Fonts

    Documentation for Custom Fonts

    Hi - just trying to get a local font to work in Blotter. My utilization of the font in CSS is as follows:

    @font-face {
      font-family: 'gtf_adieu_trialbold';
      src: url('../fonts/gtfadieutrial-bold-webfont.woff2') format('woff2');
      font-weight: normal;
      font-style: normal;

    Does blotter utilize the CSS font face somehow? Or is there something specific I need to do to make that available to JS?

    opened by heaversm 2
  • needsUpdate seems not working

    needsUpdate seems not working

    Hi, I'm new to both threejs and Blotter! After I read document about it and I intend to change some uniforms of FliesMaterial but it doesn't look like working

    The overall structure is composed with init, animate, and render function as below.

    var blotter, blotterMaterial;
    var time = 0.0;
    function init(){
      var text = new Blotter.Text("0.2", {
        family: "serif",
        size: 120,
        fill: "#171717"
      blotterMaterial = new Blotter.FliesMaterial();
      blotterMaterial.uniforms.uPointCellWidth = { type: "1f", value: 0.01 }; // (R, G, B)
      blotter = new Blotter(blotterMaterial, { texts: text });
      var scope = blotter.forText(text);
    function animate(){
    function render() {
      var delta = clock.getDelta();
      time += delta;
      if (blotterMaterial) {
        blotter.material.uniforms.uPointCellWidth.value += 0.01;
        blotter.material.needsUpdate = true;

    Based on documentation, I understood needsUpdate can be called across blotter, material and so on. I tried call needsUpdate as true with my blotter instance but also blotterMaterial but it didn't work.

    What mistake that I made? Thanks.

    opened by chaht01 2
  • Intended way to update text

    Intended way to update text

    Hey there! I'm trying to figure out how to change the text rendered on screen in a more dynamic way. Your documentation mentions a couple times that you simply just call blotter.needsUpdate = true, but it never provides an example of how to do so.

    Essentially what I'm after is an interface with a text input box. When the user hits enter on the box, the text will be sent to blotter to render. The only way I've been able to do it so far, is to keep appending new scopes to the screen. I feel like I'm missing something, or is this how you intended things to work? Here's a little example written the p5js editor. Every click will add a new scope to the window. I know that I could keep track of the old scopes and remove them as I get new submissions, but I feel like there has to be a way to tell blotter to just redraw the text texture.

    Can you give some clarity or guidance on how to do this kind of update?

    opened by aferriss 2
  • Underscore JS known vulnerability

    Underscore JS known vulnerability

    Hey love this library!

    I recently noticed a known vulnerability is being flagged up in Lighthouse (Dev Tools) as part of the inclusion of Underscore.js.

    Any chance this could be updated to patch the vulnerability?

    Screenshot 2022-12-19 at 02 39 03


    opened by jrvanstone 0
  • additional text decoration

    additional text decoration

    What other attributes are available for additional text decoration? Like if I wanted to outline the text, or fill with a gradient or even an image? Anyone have any thoughts on how to do this?

    opened by locksy 0
  • Blotter still doesn't work with custom fonts

    Blotter still doesn't work with custom fonts


    Thanks for the awesome plugin - the only issue I have is with custom fonts such as Google Fonts.

    I followed your example mentioned here, but it doesn't seem working. The strangest thing is that sometimes it works, then next time (refresh) it doesn't. The working example is here: https://twirling.space/playground/uiscape/treejs/blotter/ - Blotter still displays a serif font, but the family property is set to "'Roboto Mono'" in the text options. The init is within a document.fonts.ready callback, and the Google Fonts scripts/styles also seem to be called correctly.

    Also - while you expressed clearly that you don't intend to improve Blotter's custom font handling, I'm a little bit lost here. Blotter is all about display, style, and modern typo effects - it seems to me that if custom fonts are not fully and reliably supported, it kinda questions the purpose of the script.

    What do you think? Thanks for taking a look into this.

    opened by lunule 2
  • Very long text breaks if the Text is longer than the Viewport

    Very long text breaks if the Text is longer than the Viewport

    Hey, there you should set the style property whiteSpace to "nowrap", otherwise the Block for very long texts breaks after the Viewport width.

    // Determines size of text within the document given certain style properties

    sizeForText : function(textValue, properties) { // Using a here may not be the best approach. In theory a user's stylesheet // could override the necessary styling for determining sizes below. With growing // support for custom tags in html, we may consider using them if this raises problems. var el = document.createElement("span"), size;

    properties = this.ensurePropertyValues(properties);

    el.innerHTML = textValue; el.style.display = "inline-block"; el.style.fontFamily = properties.family; el.style.fontSize = properties.size + "px"; el.style.fontWeight = properties.weight; el.style.fontStyle = properties.style; el.style.lineHeight = properties.leading; el.style.maxWidth = "none"; el.style.padding = this.stringifiedPadding(properties); el.style.position = "absolute"; el.style.width = "auto"; el.style.visibility = "hidden"; el.style.whiteSpace = "nowrap";

    opened by anderle 0
Bradley Griffith
flea bitten
Bradley Griffith
AngularJS SPA Template for Visual Studio is a project skeleton for a simple single-page web application (SPA) built with AngularJS, Bootstrap, and ASP.NET (MVC, Web Api, SignalR).

AngularJS SPA Template for Visual Studio This project is a skeleton for a simple single-page web application (SPA) built on top of the: AngularJS 1.2.

Kriasoft 105 Jun 18, 2022
Text-based user interface CSS library

TuiCss Text-based user interface CSS library -- Documentation -- About TuiCss is a library focused to create web applications using an interface based

Vinicius Reif Biavatti 1.2k Jan 2, 2023
Apply CSS based on your browser's text rendering engine

Type Rendering Mix Type Rendering Mix website Type Rendering Mix detects your browser’s text rasterizer and antialiasing method by parsing the user ag

Bram Stein 496 Dec 6, 2022
Fluent UI web represents a collection of utilities, React components, and web components for building web applications.

Fluent UI Web ?? ?? ?? Version 8 of @fluentui/react is now available on npm! ?? ?? ?? See the release notes for more info, and please file an issue if

Microsoft 14.5k Jan 4, 2023
The most popular HTML, CSS, and JavaScript framework for developing responsive, mobile first projects on the web.

Bootstrap Sleek, intuitive, and powerful front-end framework for faster and easier web development. Explore Bootstrap docs » Report bug · Request feat

Bootstrap 161k Jan 1, 2023
700+ Pure CSS, SVG & Figma UI Icons Available in SVG Sprite, styled-components, NPM & API

700+ Pure CSS, SVG & Figma UI Icons Available in SVG Sprite, styled-components, NPM & API

Astrit Malsija 8.9k Jan 2, 2023
Chrome extension that creates a button on Lever job application pages which shows you how their api parses your resume.

EDIT I have helped make a website that provides the same functionality. Repo: https://github.com/KnlnKS/resume-parser Site: https://resume-parser.verc

Kunalan Kevin Subagaran 17 May 19, 2022
HULU Clone, View all the latest movies category wise. Built using TMDB API.

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

Hamnaikbalkhan 4 Sep 23, 2022
A set of small, responsive CSS modules that you can use in every web project.

Pure A set of small, responsive CSS modules that you can use in every web project. http://purecss.io/ This project is looking for maintainers to suppo

Pure CSS 22.7k Jan 3, 2023
A lightweight and modular front-end framework for developing fast and powerful web interfaces

UIkit UIkit is a lightweight and modular front-end framework for developing fast and powerful web interfaces. Homepage - Learn more about UIkit @getui

null 17.7k Jan 8, 2023
Modular and customizable Material Design UI components for the web

Material Components for the web Material Components for the web helps developers execute Material Design. Developed by a core team of engineers and UX

Material Components 16.6k Jan 3, 2023
Modern framework to print the web correctly.                                               

Modern framework to print web pages correctly How to use Simply include the right stylesheet(s) in your html and load it only for a printer. Gutenberg

Fabien Salathe 4.6k Dec 29, 2022
A bare-bones CSS reset for modern web development.

A modern CSS reset A tiny little reset that you can use as the basis of your CSS projects. You can read a breakdown of it here. Installation NPM: npm

Andy Bell 2.7k Jan 1, 2023
A web app landing page theme created by Start Bootstrap

Start Bootstrap - New Age New Age is a web app landing page theme for Bootstrap created by Start Bootstrap. Preview View Live Preview Status Download

Start Bootstrap 996 Dec 26, 2022
A responsive HTML template for coding projects with a clean, user friendly design. Crafted with the latest web technologies, the template is suitable for landing pages and documentations.

Scribbler - a responsive HTML template for coding projects and documentations Scribbler is a responsive HTML/CSS/Javascript template designed for deve

Amie Chen 394 Jan 1, 2023
🌆 Here I've aggregated some of the most commonly used web-page templates made using Bootstrap4 🛒

Web-page component templates using bootstrap4 Here are some of the most common web-page templates made using bootstrap4 Login page Create-Account or S

Akash Giri 89 Dec 30, 2022
A service to add web page screenshots to your Eleventy sites.

Screenshot API A runtime service to use live website screenshots on your site. Usage Image URLs have the formats: https://v1.screenshot.11ty.dev/:url/

Eleventy 91 Dec 24, 2022
A Windows 98 classic, on the web 💥

A Windows 98 classic, on the web ??

Chance Strickland 5 Aug 17, 2022