DEV Community

Cover image for Pixelate characters with canvas, and draw generative art!
Masato Ohba
Masato Ohba

Posted on • Updated on

Pixelate characters with canvas, and draw generative art!

As I posted before, I'm learning generative art with p5.js.

In this article, I introduce a tool, "String Pixelater", which I made through the learning process.

What's String Pixelater

image

This is a simple tool to pixelate any characters. pixelate means to "convert a character to a two-dimensional array" so that we can deal with the tabular data for several purposes. I actually compose this and p5.js for painting generative art.

Let's dig into how it works... It has only one API so far though!

> StringPixelater.pixelate('hello', {fontSize: 24})

[
  [0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,1,1,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0],
  [0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0],
  [0,0,1,1,1,1,1,0,0,1,1,1,1,0,0,0,0,1,1,1,0,0,0,1,1,1,1,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,1,1,1,1,0,0,0,1,1,1,1,0,0,0],
  [0,0,1,1,1,1,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,1,1,1,0,0,0,0,0,1,1,1,1,0,0],
  [0,0,1,1,1,0,0,0,0,0,0,1,1,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,0,0,0,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,1,1,1,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,1,1,1,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,0,0,0,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,1,1,1,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,1,0,0,0,0,1,1,1,1,0,0,0,1,1,0,0,0,1,1,1,0,0,0,1,1,1,1,0,0,0,1,1,1,1,0,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,0,0,0,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,1,1,0,0,0,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0]
]
Enter fullscreen mode Exit fullscreen mode

Could you vaguely see "hello" in the above?

How does it work?

In short, it uses <canvas> element as a temporary canvas to render characters. Then it extracts and parses rasterized image data. Its essential code is the following.

const canvas = <HTMLCanvasElement> document.createElement('canvas');
const context = <CanvasRenderingContext2D> this.canvas.getContext('2d');
context.fillText("hello", 0, 0);

let table = new Array(canvas.height);

const imageData = context.getImageData(0, 0, canvas.width, canvas.height);

for (let row = 0; row < canvas.height; row++){
  table[row] = new Array(canvas.width);

  for (let col = 0; col < canvas.width; col++){
    const alpha = imageData.data[(canvas.width * row + col) * 4 + 3];
    if (alpha >= 64) {
      table[row][col] = 1;
    } else {
      table[row][col] = 0;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Usage

Just install and call StringPixelater.pixelate method with a string which you want to pixelate.

$ npm install --save string-pixelater
or
$ yarn add string-pixelater
Enter fullscreen mode Exit fullscreen mode

You can use as ES module or load through <script> tag

import StringPixelater from 'string-pixelater';

const table = StringPixelater.pixelate('Hello, world');
Enter fullscreen mode Exit fullscreen mode
<script type="text/javascript" src="path/to/dist/js/string-pixelater.js"></script>
<script type="text/javascript">
  var table = StringPixelater.pixelate('Hello, world');
</script>
Enter fullscreen mode Exit fullscreen mode

transpose

If you use p5.js like me, transpose option is quite convenient because it has inverted axes,

> StringPixelater.pixelate('B', {fontSize: 24, transpose: false})
[
  [0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0],
  [0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,1,1,1,1,0,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,0,1,1,1,0,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,0,1,1,1,0,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,1,1,1,1,0,0,0],
  [0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0],
  [0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,0,1,1,1,0,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0],
  [0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0],
  [0,0,1,1,1,0,0,0,0,0,0,1,1,1,1,0,0,0],
  [0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0],
  [0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0]
]

> StringPixelater.pixelate('B', {fontSize: 24, transpose: true})
[
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
  [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
  [1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1],
  [1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1],
  [1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1],
  [1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1],
  [1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1],
  [1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1],
  [1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1],
  [1,1,1,0,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1],
  [1,1,1,1,0,0,1,1,1,1,1,0,0,0,0,0,1,1,1],
  [0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
  [0,0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,0],
  [0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
]
Enter fullscreen mode Exit fullscreen mode

The latter looks correct when it's rendered with simple and intuitive loops.

// Note: This is just pseudo code!
function draw() {
  const imageData = StringPixelater.pixelate('B', {fontSize: 24, transpose: true})
  const pixelSize = 10;

  imageData.forEach(function(row, i) {
    row.forEach(function(cell, j) {
      if (cell === 1) {
        ellipse(i * pixelSize, j * pixelSize, pixelSize, pixelSize);
      }
    })
  })
}
Enter fullscreen mode Exit fullscreen mode

This results like below:

2018-05-12 22 49 04

Artworks with String Pixelater

Just with StringPixelater.pixelate('hello'):

2018-05-12 22 49 04

We can get pixelated emoji as like StringPixelater.pixelate('🐈').

2018-05-12 22 52 36


I would be glad if anyone would have an interest in generative art or string-pixelater through this article. :)

Discussion (3)

Collapse
gmartigny profile image
Guillaume Martigny

Nice results !
I used pretty much the same technique to make Fizzle. (but without P5)

Collapse
bytegasm profile image
Bytegasm!

the idea of generative art sound pretty dope to me, any pointers to where I can start getting into it? (I'm good with css)

Collapse
ohbarye profile image
Masato Ohba Author

@torpoise
processing.org/ is the best as far as I know. "Processing" is a language and an environment to draw visual arts with code.

If you're familiar with python, processing.py (py.processing.org/) could be a better start point. In case of JavaScript, p5.js (p5js.org/).

If you'd like to try it right now, there's an interactive online editor for p5.js. Visit either of the followings, then copy & paste my code below into the editor on the page and see what will be happening.

function setup() {
  createCanvas(600, 400);
  frameRate(4)
}

function draw() {
  background(255) // clear background at every frame

  for (var i = 100; i < 400; i++) {
    var r = random(200);

    stroke(color(random(r),random(i),random(i), 60));   

    line(300, r, i, 100)
    line(300, i, i, 100)

    line(i, i, i, 300)
    line(i, i, r, 300)
    line(i, r, i, 300)

    line(500, r + 200, i + 200, i)
  }
}

Aside from that, it's exciting to search artworks on Pinterest or anything with the keyword "generative art". That definitely motivates you.