Pixelbits

Hi, I'm @jasonlong. I'm trying to make a small creative coding sketch every day(ish) to learn new things. All code is available in the GitHub repo.

I thought the Bang & Olufsen BeoSound Moment was pretty slick looking and tried to make something similar. This required manually creating an angular gradient since paper.js only supports linear and radial. Show code

<script type="text/paperscript" canvas="canvas-0021">
view.element.style.backgroundColor = '#111';

var color = new Color('#ead839');
var inc = 0.2;
var circle = new Group();
var radius = view.size.width * 0.25;

for (a=360; a>0; a-=inc) {
    color.hue += inc;
    var line = Path.Line({
        from: [0, 0],
        to: [radius, radius],
        strokeColor: color
    });
    line.rotate(a, [0, 0]);
    circle.addChild(line);
}

circle.opacity = 0.75;
circle.rotate(220);

var circleMid = new Shape.Circle([0, 0], radius * 1.41 * 0.75);

circleMid.fillColor = "#fff";
circleMid.opacity = 0.1;
circleMid.blendMode = 'color-dodge';

var circleInner = new Shape.Circle([0, 0], radius * 1.41 * 0.48);
circleInner.fillColor = "#fff";
circleInner.opacity = 0.15;
circleInner.blendMode = 'color-dodge';

var hole = new Shape.Circle([0, 0], radius * 1.41 * 0.2);
hole.fillColor = "#111";

project.activeLayer.position = view.center;
</script>

<canvas id="canvas-0021" height="300"></canvas>

I tried to recreate the background effect from this Ticketmaster redesign concept. This was the first time I've tried masking paths using boolean intersections. Show code

<script type="text/paperscript" canvas="canvas-0020">
var raster = new Raster('/pixelbits/assets/ron-burgundy.jpg');
raster.visible = false;

var boxSize = view.size.width / 50;

raster.on('load', function() {
  // Resize image to a manageable size
  raster.size = new Size(50, 50);

  for (var y = 0; y < raster.height; y++) {
    for(var x = 0; x < raster.width; x++) {
      var color = raster.getPixel(x, y);

      // bounding box
      var box = new Rectangle(new Point(x * boxSize, y * boxSize), new Size(boxSize, boxSize));
      var pathBox = new Path.Rectangle(box);

      // fill line
      var lineWidth = map(color.brightness, 0, 1, boxSize/2, 1);
      var line = new Rectangle(new Point(x * boxSize, y * boxSize - boxSize / 2), new Size(lineWidth, boxSize * 2));
      var pathLine = new Path.Rectangle(line);
      pathLine.rotate(45, new Point(x * boxSize, (y + 1) * boxSize));

      // mask the rotated fill line by the bounding box using the intersection
      newPath = pathBox.intersect(pathLine);
      newPath.fillColor = '#84184b';
      newPath.opacity = 0.5;

      pathBox.remove();
      pathLine.remove();
    }
  }
});

project.activeLayer.position = view.center;

function map(value, vMin, vMax, dMin, dMax) {
  var vValue = parseFloat(value);
  var vRange = vMax - vMin;
  var dRange = dMax - dMin;
  return (vValue - vMin) * dRange / vRange + dMin;
}

</script>

<canvas id="canvas-0020" height="300"></canvas>

Another hue flower. You can get some interesting effects just adding more layers and playing with blend modes. Show code

<script type="text/paperscript" canvas="canvas-0019">
view.element.style.backgroundColor = '#111';

var color = new Color('#0074d9');
var rectangle = new Rectangle(new Point(0, 0), new Size(100, 200));
var shape = new Path.Ellipse(rectangle);
shape.fillColor = color;

var numLayers = 3;
var blendModes = ['screen', 'screen', 'multiply'];

// make it pointy
shape.segments[1].handleIn.x = 0;
shape.segments[1].handleOut.x = 0;
shape.segments[3].handleIn.x = 0;
shape.segments[3].handleOut.x = 0;

var opacity = 0.3;
for (i = 0; i < numLayers; i++) {
    for (j = 0; j < 360; j += 15) {
        var newShape = shape.clone();
        newShape.scale(1 - (i * 0.33));
        newShape.fillColor.hue += j;
        newShape.opacity = opacity;
        newShape.blendMode = blendModes[i];
        newShape.rotate(j, [50, 220]);
    }
    opacity += 0.2;
}

shape.remove();

project.activeLayer.position = view.center;


</script>

<canvas id="canvas-0019" height="250"></canvas>

Trying out some polar coordinates to make oscillating dots. The circle sizing here is linear and would be nicer if it followed a sine wave. Show code

<script type="text/paperscript" canvas="canvas-0018">
RADIUS_MIN = 1;
RADIUS_MAX = 4;

function Ball(p, r) {
  this.radius           = r;
  this.point            = p;
  this.growing          = false;
  this.circle           = Shape.Circle(p, r);
  this.circle.fillColor = '#ed25bc';
}

Ball.prototype = {
  iterate: function() {
    if (this.growing) {
      if (this.circle.radius >= RADIUS_MAX) {
        this.growing = false;
      }
      else {
        this.circle.radius += 0.1;
        this.circle.fillColor.hue += 1;
      }
    }
    else {
      if (this.circle.radius <= RADIUS_MIN) {
        this.growing = true;
      }
      else {
        this.circle.radius -= 0.1;
        this.circle.fillColor.hue -= 1;
      }
    }
  }
}

var balls = [];

view.element.style.background = "#111";

width  = view.size.width;
height = view.size.height;
radius = RADIUS_MIN;

for (i=3; i<12; i++) {
  for (a=0; a<360; a+=15) {
    var x = Math.cos(radians(a + (i*7.5))) * (20 * i);
    var y = Math.sin(radians(a + (i*7.5))) * (20 * i);
    balls.push(new Ball(new Point(x, y), radius));
  }
  radius += 1;
}

project.activeLayer.position = view.center;

function radians(angle) {
  return (angle / 180) * Math.PI;
}

function onFrame(event) {
  for (var i = 0, l = balls.length; i < l; i++) {
    balls[i].iterate();
  }
}
</script>

<canvas id="canvas-0018" height="250"></canvas>

Derping around with sine wave phasing. Show code

<script type="text/paperscript" canvas="canvas-0017">
view.element.style.background = "linear-gradient(to bottom, #3a1d3a 0%,#e7584c 100%)";

width = view.size.width;
yMid = view.size.height / 2;

for (var i=0; i<18; i++) {
  for (var x=0; x<width; x+=3) {
    _x = (x + 500) * Math.PI/180 / 20;
    y = (Math.sin(_x*(8 - i/12)*Math.PI) +
        Math.sin(_x*(9 + i/13)*Math.PI) +
        Math.sin(_x*(10 - i/14)*Math.PI)) * 50;
    var shape = new Shape.Circle(new Point(x, y), 1);
    shape.fillColor = '#fff';
    shape.opacity = 0.7;
  }
}

project.activeLayer.position = view.center;
</script>

<canvas id="canvas-0017"></canvas>

Simple spiral experiment with circles and radians. Show code

<script type="text/paperscript" canvas="canvas-0016">
for (var i=15; i<100; i++) {
  var f = i * 0.8;
  var x = Math.cos(radians(i * 5)) * f;
  var y = Math.sin(radians(i * 5)) * f;
  var r = i * 1.6;
  var shape = new Shape.Circle(new Point(x, y), r);
  shape.strokeColor = '#aaa';
}

project.activeLayer.position = view.center;

function radians(angle) {
  return (angle / 180) * Math.PI;
}
</script>

<canvas id="canvas-0016"></canvas>

This uses a similar algorithm to our GitHub identicons. The colors stay the same, but the identicon shapes are random each time this page loads. (BTW, if you're a GitHub user, did you know that everyone has an identicon – even if you use an avatar? Just swap in your user name in this URL https://github.com/identicons/jasonlong.png. Show code

<script src="/pixelbits/js/rhill-voronoi-core.min.js"></script>

<script type="text/paperscript" canvas="canvas-0015">
  // Colors from http://clrs.cc/
  var colors = ['#0074d9', '#01ff70', '#f012be', '#ff851b', '#ffdc00'];
  var perRow = 10;
  var spriteSize = 5;
  // leave room for side gutters and spacing in between
  var pixelSize = view.size.width / ((perRow + 2) + (perRow / 2)) / 5;
  var marginSize = spriteSize * pixelSize / 2;

  view.element.style.backgroundColor = '#111';

  for (y = 0; y < colors.length; y++) {
      identicon = generateIdenticon(pixelSize, spriteSize);
      identicon.fillColor = colors[y];
      identicon.shadowColor = colors[y];
      identicon.shadowBlur = 25;

      for (x = 0; x < perRow; x++) {
          newIdenticon = identicon.clone();
          newIdenticon.position.x = x * ((pixelSize * spriteSize) + marginSize);
          newIdenticon.position.y = y * ((pixelSize * spriteSize) + marginSize);
      }
      identicon.remove();
  }

  project.activeLayer.position = view.center;

  // build ship
  var ship = new Group();
  var base = new Rectangle(
                  new Point(view.size.width / 2, view.size.height - pixelSize * 5),
                  new Size(pixelSize * 5, pixelSize));
  var turret = new Rectangle(
                  new Point(view.size.width / 2 + (pixelSize * 2), view.size.height - pixelSize * 6),
                  new Size(pixelSize, pixelSize * 2));

  ship.addChildren([new Shape.Rectangle(base), new Shape.Rectangle(turret)]);
  ship.fillColor = '#eee';

  function generateIdenticon(pixelSize, spriteSize) {
      var identicon = new Group();
      var halfAxis = (spriteSize -1) / 2;
      var i = 0;
      var x = halfAxis * pixelSize;

      while (x >= 0) {
        var y = 0;
        while (y < spriteSize * pixelSize) {
          if (Math.floor(Math.random() * 100) % 2) {
              var rect = new Rectangle(
                  new Point(x, y),
                  new Size(pixelSize, pixelSize));
              var pixel = new Shape.Rectangle(rect);
              identicon.addChild(pixel);

              if (x != halfAxis * pixelSize) {
                  xStart = 2 * halfAxis * pixelSize -x;
                  var rect = new Rectangle(
                      new Point(xStart, y),
                      new Size(pixelSize, pixelSize));
                  var pixel = new Shape.Rectangle(rect);
                  identicon.addChild(pixel);
              }
          }
          i += 1;
          y += pixelSize;
        }
        x -= pixelSize;
      }
      return identicon;
  }
</script>

<canvas id="canvas-0015" height="250"></canvas>

An initial experiment with Voronoi diagrams. The points for the cells are randomly generated and each cell has a random color brightness applied. Show code

<script src="/pixelbits/js/rhill-voronoi-core.min.js"></script>

<script type="text/paperscript" canvas="canvas-0014">
  var voronoi =  new Voronoi();

  var bbox = {
    xl: 0,
    xr: view.bounds.width,
    yt: 0,
    yb: view.bounds.height
  };

  var sites       = generateRandomPoints(70);
  var diagram     = voronoi.compute(sites, bbox);
  var baseColor   = new Color('#3d9970');
  var strokeColor = new Color(0, 0, 0, 0.2);

  if (diagram) {
    for (var i = 0, l = sites.length; i < l; i++) {
	    var cell = diagram.cells[sites[i].voronoiId];
        if (cell) {
          var halfedges = cell.halfedges,
          length = halfedges.length;
        if (length > 2) {
          var points = [];
          for (var j = 0; j < length; j++) {
            v = halfedges[j].getEndpoint();
            points.push(new Point(v));
          }
          createPath(points);
        }
      }
    }
  }

  function generateRandomPoints(count) {
    var points = [];
    for (var i=0; i<count; i++) {
      x = getRandomInt(0, view.size.width);
      y = getRandomInt(0, view.size.height);
      points[i] = {'x':x, 'y':y};
    }
    return points;
  }

  function createPath(points) {
    var path = new Path();
    path.fillColor = generateColor(baseColor);
    path.strokeColor = strokeColor;
    path.closed = true;

    for (var i=0, l=points.length; i < l; i++) {
      path.add(points[i]);
    }
    return path;
  }

  function generateColor(baseColor) {
    var newColor = baseColor;
    var adjustment = getRandomInt(0, 10);
    newColor.brightness += adjustment / 1000;
    return newColor;
  }

  function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min)) + min;
  }

</script>

<canvas id="canvas-0014"></canvas>

I recreated the smoke effect I made for http://bounty.github.com with Paper.js. It is a fairly straightforward particle emitter using circles whereas the Bounty site uses a png image. The effect here isn't quite as nice, but could probably be made better with some further variable tweaking. Show code

<script type="text/paperscript" canvas="canvas-0013">
  view.element.style.backgroundColor = '#111111';

  var NUM_PARTICLES  = 100;
  var EMITTER_X      = view.size.width / 2;
  var EMITTER_Y      = view.size.height - view.size.height * 0.2;
  var MIN_SIZE       = 1.5;
  var MAX_SIZE       = 2.5;
  var MIN_VELOCITY_Y = 0.2;
  var MAX_VELOCITY_Y = 0.8;
  var VELOCITY_X     = 0;
  var MIN_ALPHA      = 0.25;
  var MAX_ALPHA      = 0.45;
  var FADE_SPEED     = 0.001;
  var GROWTH_SPEED   = 1.005;
  var WIND_SPEED     = 0.13;

  var particles      = [];

  function SmokeParticle(x, y, visible) {
    this.circle = new Shape.Circle({
      center: [x, y],
      radius: (Math.random()*(MAX_SIZE - MIN_SIZE)) + MIN_SIZE,
      fillColor: '#333',
      shadowColor: '#777',
      shadowBlur: 70,
      visible: visible
    });

    this.circle.fillColor.alpha = (Math.random()*(MAX_ALPHA - MIN_ALPHA)) + MIN_ALPHA;

    this.velX      = VELOCITY_X;
    this.velY      = (Math.random()*(MAX_VELOCITY_Y - MIN_VELOCITY_Y)) + MIN_VELOCITY_Y;
    this.reborn    = false;

    this.isDead = function() {
      return this.circle.fillColor.alpha === 0;
    }

    this.update = function() {
      this.circle.position.x  += this.velX + WIND_SPEED;
      this.circle.position.y  -= this.velY;
      this.circle.radius  *= GROWTH_SPEED;
      this.circle.fillColor.alpha -= FADE_SPEED;
      if (this.circle.fillColor.alpha < 0) this.circle.fillColor.alpha = 0;
    }
  }

  cig = new Shape.Circle({
    center: [EMITTER_X, EMITTER_Y+2],
    radius: 2,
    fillColor: '#ee6f37',
    strokeColor: '#f00',
    shadowColor: '#f00',
    shadowBlur: 12
  });

  for (var i = 0; i < NUM_PARTICLES; i++) {
    var particle = new SmokeParticle(EMITTER_X, EMITTER_Y, false);
    particles.push(particle);
  }

  function onFrame(event) {
    for (var i = 0; i < NUM_PARTICLES; i++) {
      p = particles[i];

      if (p.isDead()) {
        p.circle.remove();
        particles[i] = new SmokeParticle(EMITTER_X, EMITTER_Y, true);
      }

      p.update();
    }
  }
</script>

<canvas id="canvas-0013"></canvas>

Random arc grid inspired by the way more awesome Is this art? project by James Mellers. Show code

<script type="text/paperscript" canvas="canvas-0012">
  view.element.style.backgroundColor = '#85144b';
  arcSize = view.size.height / 12;

  // Start with a circle...
  var arc = new Path.Circle(new Point(100, 70), arcSize);

  // And trim it down to a quarter circle.
  arc.removeSegment(3);
  arc.segments[0].handleIn.y = 0;
  arc.segments[1].handleOut.x = 0;
  arc.segments[2].handleIn.x = 0;
  arc.segments[2].handleOut.y = 0;
  arc.segments[2].point.x = 100;
  arc.fillColor = '#ffffff';

  for (y=0; y<10; y++) {
    for (x=0; x<10; x++) {
      newArc = arc.clone();
      newArc.position = new Point(x*arcSize, y*arcSize);

      switch (getRandomInt(0, 3)) {
        case 0:
          newArc.rotate(0);
          break;
        case 1:
          newArc.rotate(90);
          break;
        case 2:
          newArc.rotate(180);
          break;
        case 3:
          newArc.rotate(270);
      }

      opacity = getRandomInt(40, 95);
      if (opacity < 45) {
        opacity = 0;
      }
      newArc.fillColor.alpha = opacity/100;
    }
  }

  arc.remove();

  project.activeLayer.position = view.center;

  function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min)) + min;
  }
</script>

<canvas id="canvas-0012" height="250"></canvas>