September 26th, 2014

Contrast Validation with Node.js

Author: Noah Portnoy

When customers create business cards, postcards, and other products on the Vistaprint web site, they often begin with a design template provided by Vistaprint. Design templates provide a look and feel for your product, including a background image, a color scheme, text boxes for your name and address, and so on.

During my summer internship at Vistaprint this year, I had the opportunity to solve a long-standing, practical problem with design templates, using a JavaScript platform called Node.js. In this article, I’ll describe the challenge and the solution.

The problem

Design templates are created by artists at Vistaprint using Photoshop. Customers can then personalize those designs using Vistaprint’s JavaScript-powered Studio that is hosted on Vistaprint’s website. Studio has a special feature that checks the contrast between text and background to make sure the text is readable, e.g., you didn’t put white text on a white background. This feature is called contrast validation. The problem is that Photoshop doesn’t perform contrast validation, so it’s possible for a Vistaprint designer to create a template design that will violate Studio’s validation rules. Here an example of such a violation, where white text overlaps a white or light-colored background, resulting in poor readability.


My challenge was to identify, out of Vistaprint’s inventory of about 1.6 million template designs, every design that has contrast issues. The best way to approach this would be to take the same validation rules used to validate customer-personalized designs in Studio and apply them to all of Vistaprint’s template designs on the server. In the past, this would have been a challenge, since the code for Studio’s contrast validation is written solely in JavaScript. Fortunately, Node.js allows you to execute JavaScript code on the server.

The solution

I created an automated contrast-validation tool using Node.js that can validate template designs like the tie-dye one above. The benefit here is that we can catch contrast issues with Vistaprint’s template designs before they are rolled out to production.

How it works

So, you might be asking, how does this tool work?


First, the Design Validator reads a list of template design IDs from a text file.

From there, the Design Validator makes asynchronous requests to Studio, grabbing everything that it needs to perform contrast validation on the provided designs. These retrieved elements include images, where each image contains one design element, such as a text field, a picture, or the design’s background. The tool also fetches JSON data for each design, where a design’s JSON file contains all positional information about how the retrieved design elements are laid out.

The Design Validator then generates canvases for each design using the Node canvas package, which emulates the HTML5 canvas element that is used in Studio. The generated canvases for a given design come in pairs, where a foreground canvas contains a design element that sits “on top” of the design, like a text field, and the corresponding background canvas contains everything “behind” that text foreground element. The downloaded JSON data is essential in creating canvases that are identical to those in Studio.

Once all canvases are generated, the Design Validator passes the canvases to Studio’s contrast validation. Thanks to Node.js, the tool is able to execute this JavaScript code and receive the validation results. Any contrast errors or warnings are then written to a CSV file along with information about which designs, and more specifically which design elements, failed validation.

Challenges encountered

Problem 1: validation results differ slightly

About halfway through this project, I discovered that my tool’s contrast validation results were mysteriously different—very slightly—from that of Studio’s contrast validation. The foreground and background canvases generated by my program certainly looked exactly the same as what was being rendered in Studio, as shown below:



So why did my program provide different contrast validation results for these two images? Well, it turned out that the images are different if you check pixel by pixel.


A highlighted pixel above indicates that its RGB value differs between the Design Validator and Studio-rendered images. So the solution here was to first blame the Node canvas package for not perfectly emulating HTML5’s canvas element, and then more practically, tweak the thresholds of contrast validation to account for this rendering discrepancy.

Problem 2: working with asynchronous JavaScript in Node.js

One component of the Design Validator involves requesting from Studio all the images and JSON data for every design. Here’s one of the first approaches I took to perform these requests:

Each function is given the design IDs that should be validated. The getJson() function iterates through the ID list, and for each ID it requests that design’s JSON data from Studio. The getImages() function does the same thing for fetching images. Sounds good, right?

It turns out that this is an inefficient way of requesting data from a website. Using this approach, the program looks at the first design ID and requests its JSON data. The Design Validator waits, doing nothing, until it gets a response from the server. Then it looks at the second design ID and requests its JSON data, etc. Once all JSON data has been fetched, the Design Validator then iterates through the design IDs and does the same thing for image requests. So how can we make this faster, reducing as much as possible the idle time spent waiting for responses from the server?

In JavaScript, all code is executed by a single event thread; nothing is ever executed in parallel. Nevertheless, we can use asynchronous behavior in JavaScript to make great improvements in execution time. I’ve used the Async.js module in the Design Validator’s code to achieve this.

Let’s first look at a simple example of asynchronous code.

This code calls the getJson() function on a single design. Once the getJson() function has reached the end of its execution, it calls the anonymous function passed to it. Here’s the anonymous function on its own so you can see it more easily.

This function has not been given an identifier, hence the name “anonymous”. It is passed as the second argument to the getJson() function. So when executed, this code block will call getJson(), which will fetch the JSON data for a design, and then the line “JSON data downloaded and processed” will be printed out to the console.

Once I wrapped my head around asynchronous functions, I started improving how the Design Validator performs its requests.

Let’s take a look at how this works. The async.parallel() function runs the two inner functions in parallel, without waiting for either to complete before running the other. This allows the program to, say, make a request for a design’s JSON data, and then while waiting for a response, make another request for that design’s images. Now the program will wait for a response from either request; when a request get a response from the server, the overarching JavaScript event thread steps into that request’s respective code. For example, if the JSON request gets a response first, code within the getJson() function will be executed and the JSON data will be processed. When either the getJson() or getImages() function has completed, the anonymous function passed to it is executed. In this case, these anonymous functions are callbacks; when one of these callbacks is called, it alerts the async.parallel() function that its respective function has finished.

My final version of this code goes a step further, so the Design Validator can process all of the requested designs. Can you figure out how it works? Check out the Async.js documentation if you want to learn more.

What I learned

Working with JavaScript was definitely interesting. Despite the fact that it has a C-based syntax, it’s radically different from some of the more managed languages I’ve used, namely because of lexical scoping, closures, single-thread event loop, etc. Building a server-side JavaScript application was a great experience and an interesting new skill to add to my repertoire. I had an awesome internship at Vistaprint, solving an interesting problem using technologies that I hadn’t used before.

Noah Portnoy spent the summer of 2014 as a software engineering intern at Vistaprint, working on the Studio team. He is a senior majoring in Computer Systems Engineering at the University of Massachusetts, Amherst. In his spare time, Noah builds fire-fighting robots.

Recent Posts

Join Life in Vistaprint Search Jobs