Published
Edited
Mar 16, 2020
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
// Create width and height variables
const width = 600;
const height = 300;
// Create a HTML5 canvas object
const canvas = DOM.canvas(width, height);
// Now add a new paper variable to the global scope to reference the PaperScope object.
const p = new paper.PaperScope();
// Using the setup method create a project with the HTML canvas element. This can also be combined with the step above
paper.setup(canvas);
// Assign multiple variable names to the p object
const { Path, Point, Color } = p;
// Create a background object
let background = new p.Path.Rectangle([0, 0], [width, height]);
// Give the background a colour
background.fillColor = color1;

// Place 500 instances of the circle randomly placed and random radius between 0 and 10
for (let i = 0; i < 200; i++) {
let circle = new p.Path.Circle({
center: [ Math.random()*width,Math.random()*height],
radius: Math.random()*10,
fillColor: color2,
opacity:0.95
});
}


return canvas;
}

Insert cell
Insert cell
{
// Create width and height variables
const width = 600;
const height = 300;
// Create a HTML5 canvas object
const canvas = DOM.canvas(width, height);
// Now add a new paper variable to the global scope to reference the PaperScope object.
const p = new paper.PaperScope();
// Using the setup method create a project with the HTML canvas element. This can also be combined with the step above
paper.setup(canvas);
// Assign multiple variable names to the p object
const { Path, Point, Color } = p;
// Create a background object
let background = new p.Path.Rectangle([0, 0], [width, height]);
// Give the background a colour
background.fillColor = color1;


let circle = new p.Path.Circle({
center: [width/2,height/2],
radius: 50,
fillColor: 'blue',
strokecolour: color,
opacity:0.65
});
// Create a symbol definition from the circle:
let definition = new p.SymbolDefinition(circle);
const num=1000;

// Place 100 instances of the symbol definition:
for (let i = 0; i < num; i++) {
// Place an instance of the symbol definition in the project:
let instance = definition.place();
// Move the instance to a random position within the view:
instance.position = [Math.random()*width,Math.random()*height] ;
// Scale the instance between 0.25 and 1:
instance.scale(Math.random());
}
// http://paperjs.org/reference/view/#onframe
circle.onFrame = function(event) {
// Every frame,
// Loop through the segments of the path:
for (let i = 0; i <= num; i++) {
// Move the item 1/20th of its width to the right. This way
// larger circles move faster than smaller circles:
this.position.y += this.bounds.height / 50000;
}
}
return canvas;
}

Insert cell
Insert cell
{
const height = 400
const mean = width / 2
const sd = width / 10
const context = DOM.context2d(width, height)
let x = width / 2
let y = height /2
let r = 20
// Order matters, so we set the fillStyle to our palette light blue color before the html fillRect which draws a rectangle with the https://www.w3schools.com/tags/canvas_fillrect.asp
context.fillStyle = color1 //'rgba(5, 237, 245, 0.9)'
context.fillRect(0, 0, width, height)
for (let i = 0; i < 500; ++i) {
//Returns a function for generating random numbers with a normal (Gaussian) distribution centered in the context around the mean and sd defined above.
x = normal() * sd + mean
context.beginPath()
context.fillStyle = 'rgba(5, 53, 245, 0.05)' // which is a more transparent 'blue'
context.arc(x, y, r, 0, 6)
context.fill()
yield context.canvas
}
}
Insert cell
Insert cell
{
const height = 500
const radius = 50
const ctx = DOM.context2d(width, height)
for (let i = 0; i < 900; ++i) {
const x = normal() * 80 + 0.5 * width
const y = normal() * 80 + 0.5 * height
const clr = splatterColorSigma !== 0 ? rndSplatterColor() : splatterColorByPos(x - width / 2, y - height / 2, Math.max(width, height) / 2)
ctx.beginPath()
ctx.fillStyle = clr
ctx.arc(x, y, radius, 0, 6)
ctx.fill()
yield ctx.canvas
}
}
Insert cell
md`**Day 84**

I have been wanting to make some noise for a while.

Create [Perlin Noise](https://en.wikipedia.org/wiki/Ken_Perlin) with the [simplex-noise.js](https://github.com/jwagner/simplex-noise.js) library.

`
Insert cell
{
const height = 300
const context = DOM.context2d(width, height)

let steps = 0;
while(true) {
let t = steps *0.02
let x = steps % width*2
let y = height * (1 - noise1D(t))
context.fillStyle = color1 //'rgba(5, 237, 245, 0.9)'
context.clearRect(x, 1, 1, height)
context.fillRect(x, y, 1, 10)
++steps
yield visibility(context.canvas)
}
}
Insert cell
Insert cell
{
const height = 400
const width = 600
const context = DOM.context2d(width, height)
const clr = tinycolor(color1).toHsv()
for (let x = 0; x < width; ++x) {
for (let y = 0; y < height; ++y) {
const v = noise2D(x *1 , y *0.35)
context.fillStyle = tinycolor(_.defaults({v}, clr)).toRgbString()
context.fillRect(x, y, 10, 10)
}
}
return context.canvas
}
Insert cell
md`**Day 86**

Now move onto animating using :

> the most basic building block for programming motion—the vector

and

> assume it refers to a Euclidean vector, defined as an entity that has both magnitude and direction.
Following the [vector chapter](https://natureofcode.com/book/chapter-1-vectors/) in the book.

using the 3D library THREE.js. We will start with a 2D animation using vectors, [vector2](https://threejs.org/docs/index.html#api/en/math/Vector2).

This animation still uses the DOM.context2d and uses location and velocity objects, where
> the algorithm for motion, location = location + velocity
However the
> addition operator + is reserved for primitive values
so we can use an add() method/ function for vectors to add the x's and y's together.
`
Insert cell
{
const height = 400
const width = 600
const context = DOM.context2d(width, height)
const radius = 15
// Define location object of the 2D vector with the x and y properties
const location = new THREE.Vector2(50, 50)
// Define the xspeed and yspeed as properties of velocity object
const velocity = new THREE.Vector2(2, 2)
// Set the fill colour of the ball in the context
context.fillStyle = color1
while (true) {
// update the location vector with the velocity to create motion
location.add(velocity);
// By including the radius in the edge calculation, the ball seems to bounce off the edge else it disappears into the edge of the context by the radius
if (location.x < radius || location.x > width - radius) {
velocity.x = -velocity.x
}
if (location.y < radius || location.y > height - radius) {
velocity.y = -velocity.y
}
// render the 'new' ball by clearing the background with each frame
context.clearRect(0, 0, width, height)
// start a new circle with the new x and y locations, so it appears to be moving
context.beginPath()
context.arc(location.x, location.y, radius, 0, PI * 2)
context.fill()

yield context.canvas
}
}
Insert cell
md`**Day 87**

Change the colours so that the ball becomes a colouring in line that bounces off the walls`
Insert cell
{
const height = 400
const width = 600
const context = DOM.context2d(width, height)
const radius = 50
// Define location object of the 2D vector with the x and y properties
const location = new THREE.Vector2(50, 50)
// Define the xspeed and yspeed as properties of velocity object
const velocity = new THREE.Vector2(5, 5)
context.fillStyle = color2 //'rgba(5, 237, 245, 0.9)'
context.fillRect(0, 0, width, height)
// Set the fill colour of the ball in the context
context.fillStyle = color1
while (true) {
// update the location vector with the velocity to create motion
location.add(velocity);
// By including the radius in the edge calculation, the ball seems to bounce off the edge else it disappears into the edge of the context by the radius
if (location.x < radius || location.x > width - radius) {
velocity.x = -velocity.x
}
if (location.y < radius || location.y > height - radius) {
velocity.y = -velocity.y
}
// render
//context.clearRect(0, 0, width, height)
// start a new circle with the new x and y locations, so it appears to be moving
context.beginPath()
context.arc(location.x, location.y, radius, 0, PI * 2)
context.fill()

yield context.canvas
}
}
Insert cell
md`**Day 88**

Change `
Insert cell
{
const height = 400
const width = 600
const context = DOM.context2d(width, height)
const mid = new THREE.Vector2(0, height )
// Draw a line to represent the vector
context.translate(mid.x, mid.y)
context.strokeStyle=color1
const mouse = new THREE.Vector2()
context.canvas.onmousemove = (e) => {
mouse.set(e.offsetX, e.offsetY).sub(mid)
}
while (true) {
context.clearRect(-mid.x, -mid.y, width, height)
context.beginPath()
context.lineWidth = 4
context.moveTo(1,0)
context.lineTo(mouse.x, mouse.y)
context.stroke()
yield context.canvas
}
}
Insert cell
md`Day 89

A take on this plot to create rippling hexs voronoi.

https://observablehq.com/@mbostock/shimmy`
Insert cell
height=400
Insert cell
performance.now()
Insert cell
{
const height = 400
const width = 600
const context = DOM.context2d(width, height);
const coordinates = new Float64Array(points.length * 20);
const voronoi = new d3.Delaunay(coordinates).voronoi([0, 0, width, height]);
context.fillStyle = color2 //'rgba(5, 237, 245, 0.9)'
context.strokeStyle =color1
context.fillRect(0, 0, width, height)
while (true) {
const z = performance.now()* 0.001;
context.clearRect(0, 0, width, height);
for (let i = 0, j = -1; i < points.length; ++i) {
const [x, y] = points[i];
const t = noise(x * config.f, y * config.f, z) ;
coordinates[++j] = x + config.l;
coordinates[++j] = y + config.l * Math.sin(t);
}
context.beginPath();
voronoi.update().render(context);
context.stroke();
yield context.canvas;
}
}
Insert cell
noise = {
const simplex = new (await require("simplex-noise@2"));
return simplex.noise3D.bind(simplex);
}
Insert cell
viewof config = {
const form = html`<form style="font: 12px var(--sans-serif);">
<label style="display: block;">
<input name=s type=range min=1 max=40 value=10 step=0.1 style="width:180px;">
cell spacing = <output name=os></output>
</label>
<label style="display: block;">
<input name=l type=range min=1 max=200 value=100 step=1 style="width:180px;">
displacement = <output name=ol></output>
</label>
<label style="display: block;">
<input name=f type=range min=0.001 max=0.005 value=0.002 step=0.0001 style="width:180px;">
frequency = <output name=of></output>
</label>
</form>`;
form.oninput = () => {
form.value = {
s: form.s.valueAsNumber,
l: form.l.valueAsNumber,
f: form.f.valueAsNumber
};
form.os.value = `${form.value.s.toFixed(1)}px`;
form.ol.value = `${form.value.l.toFixed(0)}px`;
form.of.value = `${form.value.f.toFixed(3)}`;
};
form.oninput();
return form;
}
Insert cell
points = d3.hexbin()
.radius(config.s)
.extent([[-config.l, -config.l], [width + config.l, height + config.l]])
.centers()
Insert cell
d3 = require("d3-selection@1.4.0", "d3-array@2", "d3-hexbin@0.2", "d3-scale-chromatic@1.5", "d3-shape@1.3.5", "d3-scale@3.1.0","d3-random","d3-hexbin@0.2", "d3-delaunay@5")
Insert cell
PI = Math.PI
Insert cell
THREE = require('three')
Insert cell
_ = require('lodash')
Insert cell
noise2D = (x, y) => (simplex.noise2D(x, y) + 1) * 0.5
Insert cell
noise1D = (x) => (simplex.noise2D(x, 1) + 1) * 0.5
Insert cell
simplex = new SimplexNoise(random)
Insert cell
noise3D = (x, y, z) => (simplex.noise3D(x, y, z) + 1) * 0.5
Insert cell
SimplexNoise = require('simplex-noise')
Insert cell
viewof splatterColorSigma = slider({
min: 0,
max: 60,
step: 1,
value: 0,
display: v => v ? v : 'Color will be based on distance to center',
description: "choose color variance, or set to 0 to bind variance with position"
})
Insert cell
splatterColorMean = "#05ecf6"
Insert cell
rndSplatterColor = () => tinycolor(splatterColorMean).spin(Math.floor(normal() * splatterColorSigma)).toHslString()
Insert cell
import {slider, color, radio} from '@jashkenas/inputs'
Insert cell
tinycolor = require("tinycolor2")
Insert cell
splatterColorByPos = (x, y, max) => tinycolor(splatterColorMean).spin(Math.floor(360 * Math.sqrt(x * x + y * y) / max)).toHslString()

Insert cell
normal = d3.randomNormal.source(random)()
Insert cell
random = seedrandom()
Insert cell
seedrandom = require("seedrandom/seedrandom.min.js")
Insert cell
import {colorPicker} from "@shaunlebron/color-picker"
Insert cell
paper = require("paper");
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more