i wrote some javascript that simulates a mondrian painting. a friend was discussing the difficulty of simulating the random continuity of the lines in his paintings, and i realized how easy it would be to simulate everything else. it also sounded like a fun little challenge!

so here’s my code. feel free to fork it on github. here’s what the final result looks like:

(open in a new tab)

here’s an example of mondrian’s work in case you’re not familiar:
mondrian book cover

here is the javascript from my app:

 * mondrian.js
 * @Author Greg Doolittle
 * description: generates a mondrian-esque grid with randomly filled squares.

function Mondrian( lineWidth, squareCount, density ) {
    this.lineColor = "#0a0a0a";
    this.lineWidth = lineWidth || ( window.innerWidth <  window.innerHeight) ? Math.round( window.innerWidth / 40 ) : Math.round( window.innerHeight / 40 );
    this.density = density || .5;
    this.squareCount = squareCount || 5;
    this.body = document.getElementsByTagName( "body" )[0];

    this.button = document.createElement( "button" );
    this.button.id = "play";
    this.button.innerHTML = "Next";
    this.body.appendChild( this.button );
    document.getElementById( "play" ).addEventListener( "click", function() {
    this.canvas = document.createElement( "canvas" );
    this.canvas.width = window.innerWidth - 2 * this.lineWidth;
    this.canvas.height = window.innerHeight - 2 * this.lineWidth;
    this.context = this.canvas.getContext( "2d" );
    this.context.lineWidth = this.lineWidth;
    this.body.appendChild( this.canvas );
    this.canvas.style.border = this.lineWidth + "px solid "+ this.lineColor;
    this.context.fillStyles = ["#cc0000", "#ffdd11", "#0066bb", "#000000"]; // mondrian's standard colors
    // this.context.fillStyles = ["#cc0000", "#ff5f42", "#870006", "#000000"]; // monochrome in red
    // this.context.fillStyles = ["#f59331", "#3fb34f", "#615ea6", "#000000"]; // eew... secondaries

Mondrian.prototype.paint = function () {
    this.context.clearRect( 0, 0, this.canvas.width, this.canvas.height );
    this.xrow = RandomSet( this.squareCount, this.canvas.width, this.lineWidth );
    this.yrow = RandomSet( this.squareCount, this.canvas.height, this.lineWidth );

    // draw squares
    for ( i = 0; i <= this.squareCount; i++ ) {
        var j = 0;
        if ( Math.random() > this.density ) {
            this.context.fillStyle = this.context.fillStyles[ Math.floor( Math.random() * this.context.fillStyles.length )];
            this.context.fillRect( this.xrow[i - 1 ] + this.lineWidth / 2, 0, this.xrow[i] - this.xrow[i - 1] - this.lineWidth, this.yrow[j] );
            this.context.strokeStyle = this.lineColor;
        for ( j; j <= this.squareCount; j++ ) {
            if ( Math.random() > this.density ) {
                this.context.fillStyle = this.context.fillStyles[Math.floor( Math.random() * this.context.fillStyles.length )];
                this.context.fillRect( this.xrow[i - 1 ] + this.lineWidth / 2, this.yrow[j - 1 ] + this.lineWidth / 2, this.xrow[i] - this.xrow[i - 1] - this.lineWidth, this.yrow[j] - this.yrow[j - 1] - this.lineWidth );
                if ( i === 0 ) {
                    this.context.fillStyle = this.context.fillStyles[Math.floor( Math.random() * this.context.fillStyles.length )];
                    this.context.fillRect( 0, this.yrow[j - 1 ] + this.lineWidth / 2, this.xrow[i], this.yrow[j] - this.yrow[j - 1] - this.lineWidth );

    // draw grid
    for ( each in this.xrow ) {
        this.context.moveTo( this.xrow[each], 0 );
        this.context.lineTo( this.xrow[each], this.canvas.height );
        this.context.strokeStyle = this.lineColor;
    for ( each in this.yrow ) {
        this.context.moveTo( 0, this.yrow[each] );
        this.context.lineTo( this.canvas.width, this.yrow[each] );
        this.context.strokeStyle = this.lineColor;

function sortNumber( a, b ) {
    return a - b; // has to add this because javascript can't natively sort arrays of integers. #wtfjavascript
function randomSign() {
    if ( Math.random() > .5 ) {
        return 1;
    } else {
        return -1;

function RandomSet( length, range, lineWidth ) {
    // rather than use a truly random set, this creates a list of evenly spaced numbers, then adds a modifier that helps spread the grid out more *mondrian-ishly*
    var i = 1, result = new Array();
    result[0] = 0 - lineWidth;
    for ( i; i < length; i++ ) {
        result[i] = Math.round( i * range/length + randomSign() * Math.ceil( Math.random() * range / length ) );
    result = result.sort( sortNumber );
    var i = 0;
    for ( i; i < length; i++ ) {
        if ( i > 0 ) {
            if ( result[i] - result[i - 1] < 3 * lineWidth ) {
                result.splice( i, 1 );
    result[result.length] = range + ( 2 * lineWidth );
    return result;

var magicCanvas = new Mondrian();

i will admit that one of the characteristics of mondrian’s work which i did not include in my simulator, is that his lines are frequently segmented — meaning that they frequently don’t stretch entirely from edge to edge of the canvas. if i revisit this little project, i would like to add this logic.


i had the chance to sit in on this year’s Environment for Humans Accessibility Summit with my company. there were about a dozen of us in a conference room, huddled around a conference table watching presentations on a projector, and discussing the talks during the breaks in between. there were some great presentations from Elle Waters, Patrick Fox, Shawn Lauriat, Luis Perez, Dennis Lembree, and Aaron Gustafson. i highly recommend this event to all web professionals, both for people who don’t know how to make their content accessible, as well as those who have the knowledge, but still need help making the case to convince business owners to spend time on accessibility as a standard part of their team’s development lifecycle.


yesterday i read this headline: Airline passengers refuse to fly after a blind man and his guide dog are removed from the plane

Airline passengers rallied around a blind man Wednesday night after he and his guide dog were removed from the plane.

The flight was delayed for about an hour and a half on the tarmac, and the dog became restless, and other passengers said a flight attendant ordered Rizzi and his guide dog off the plane.

The rest of the passengers banded together and said they refused to fly unless the man and his dog were permitted back onto the plane.

it was nice to see the rest of the passengers sticking up for this guy.

that same day, i attended a conference on accessibility for the web. the web is a visual medium for so many people. but imagine if you experienced the web without sight. the conference was focused on building sites for users with vision impairments. i can’t imagine how challenging it would be to lose my sight, or to be born without sight, but there are a lot of things we can easily do on our websites to improve the experience for these users, and in doing so, we often improve the site for all of our users. as people who create websites, there really is no argument you can make against striving for accessibility.

do your part within your organization: build accessible web experiences, and encourage your colleagues to do the same.


i’m playing trumpet for the producers with WCT this fall. this show is one of my favorites. it’s so offensive though… in fact so offensive that i don’t really feel comfortable inviting co-workers, though i did invite my friends. it is “guaranteed to offend all races, creeds, and religions.” lots of fun jazz licks, and i’m playing the lead trumpet part. this one is a blast.

come see the show! www.woodsidetheatre.com


when asked to play this show, i was reluctant. but i did it, and i’m glad. it’s a good show. the humor is very monty python.

come see us at stage1theater in newark!


i’m playing trumpet for the sound of music with WCT . great cast, great set, great orchestra, great show.

come see us! http://www.woodsidetheatre.com/


I recently posted about my new job at Eveo — fast-forward to now (5 months later), and I’m starting yet another new job. It’s not really my style to skip from one company to another (before Eveo I stayed at one company for 4 years, and before that I stayed for almost 3 years), but I came across a great opportunity at Apple that I simply couldn’t pass up. My boss was very understanding. It’s not like I was leaving Eveo to work for a different agency. I had to leave, to pursue a role as a Web UI Engineer at Apple.


i like the idea for this app, because it ties together fitness, money, and games. the way it works sounds pretty straightforward. first you declare a workout regimen, and then get paid for sticking to it (or get fined for breaking it!).

do i know anybody who’s used gympact?

gympact screenshot


i’ve been working at Eveo for a little over 2 months now, and so far i’ve worked on over 10 projects. some of the projects are webkit-only, which has provided the opportunity to become better-acquainted several new CSS3 attributes. the non-webkit projects that i’ve worked on are broadening my understanding about other stacks/platforms. here are some of the things i’ve used lately:
- apple-specific meta tags:

<meta name="apple-mobile-web-app-capable" content="yes"/>
<meta name="apple-mobile-web-app-status-bar-style" content="black"/>

- the ‘viewport’ meta tag:

<meta name="viewport" content="user-scalable=yes, width=device-width, initial-scale=1.0, maximum-scale=2.0"/>

- groovy/gsp
- magnolia CMS, and FreeMarker templates
- QT Webkit
- object literal notation
- touchstart/touchmove/touchend vs. mousedown/mouseup javascript event listeners
- css animations:

using translate3d(x, y, z) and translateZ(z) to force hardware acceleration:

-webkit-transform: translate3d(0,0,0);

multi-state animations:

-webkit-keyframes animationName { 0%,100% {...} 50% {...} }
.animatedState { -webkit-animation: animationName duration delay iterations direction fillMode }

transition animations:

-webkit-transition: attributeName duration timingFunction delay;

using bezier curves for easing:

 -webkit-animation-timing-function: cubic-bezier(x1,y1,x2,y2);

sprite animations:

 -webkit-animation-timing-function: step-start;

- other css3 stuff:

- box-shadow
- box-shadow:inset
- display: table, etc. (not too proud of this one)
- background-image: -webkit-gradient
- multiple background images
- background clip
- font-family (and debugging OTF and TTF font issues for QT-Webkit)
- transform:scale
- transform-origin
- nth-child()
- ::before & ::after
- @media queries, by orientation and resolution

it’s nice that at Eveo, i don’t have to do as much browser support as my last company. this allows me to take advantage and learn more about advanced css. it’s a good place for me to learn about developing for webkit, see how an agency works, and keep my skills up-to-date. i’m definitely taking on new challenges in terms of client-side code, but feel like i’ve lost some momentum on the server-side of the coin. as my new-guy aura wears off, maybe i can start pushing for better server technology. where it stands now, i don’t want to rock the boat too hard. overall, it’s a nice place to work, and a good company to work for.


task: apply a gradient fill to some text.

method: background-clip

h1 {
background: -webkit-linear-gradient(top,
#252525 0%,
#4c4c4c 50%,
#010101 51%,
#505050 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;

result: works great in safari and chrome, but not so great in QT-webkit. QT had a very interesting interpretation of what the background gradient should do. the result was black text, with the gradient background filling the negative space outside of the text — not the effect we were looking for.

my work-around: mask-image
first, convert hex colors to rgba values based on a single color, in this case, based on white:
#252525 = rgba(37, 37, 37, 1)
rgba(37, 37, 37, 1) = rgba(1, 1, 1, 0.145098039)

then use those colors in a mask image:

h1 {
-webkit-mask-image: -webkit-linear-gradient(top,
rgba(1, 1, 1, 0.145) 0%,
rgba(1, 1, 1, 0.298) 50%,
rgba(1, 1, 1, 0.003) 51%,
rgba(1, 1, 1, 0.313) 100%);

caveats: this is fairly straightforward for monochrome gradients. using multiple colors will require some very complicated color calculations.