p5js with AI 100日チャレンジ

これは何?
こちらの書籍を知って読んでみて触発されたので,自分もプロンプトのみでコーディングをしてどこまでいけるのか?を試してみたくなった次第.
もちろんやりたいのは p5.js を用いた Generative Art.
ルール
- どの AI モデルを使っても良い
- 極力 AI が吐き出したコードに手を加えない(あくまでプロンプトのみ)
- うまくいかなかったとしても,時間が来たらその時点での作品とコードをここに晒す
- 少しでも変化をつけ,全く同じなものは作らない

Day1: Door like shapes
まずは格子状にシェイプ(△,□,◯)を並べ,時間経過とともに小さくなったり,本のサイズに戻るようなアニメーションをする幾何学アート.吐かれたコードを自分で手直しをすれば頭の中の完成品になるんだが,あえてプロンプトのみでコーディングをしてみたかったので,完成には至らず.だいぶ気持ちの悪い挙動になってしまった…
ちなみに今日は Cursor
の Agent で Auto-select
で書いてみた.
コード
// Grid parameters
const gridSize = 10; // Number of cells in each row/column
let cellSize;
let shapes = [];
let colors = [];
let currentPalette = 'Hokusai'; // You can change this to any palette name from colorPalette.js
let globalTime = 0;
function setup() {
createCanvas((W = min(windowWidth, windowHeight) - 50), W);
rectMode(CENTER);
cellSize = W / gridSize;
// Select 3 colors from the chosen palette
const palette = colorPalette.find((p) => p.name === currentPalette);
if (palette) {
// Take the first 3 colors from the palette
colors = palette.colors.slice(0, 3);
} else {
// Fallback colors if palette not found
colors = ['#074A59', '#F2C166', '#F28241'];
}
// Initialize shapes
for (let i = 0; i < gridSize; i++) {
for (let j = 0; j < gridSize; j++) {
// Randomly choose shape type (0: square, 1: triangle, 2: circle)
const shapeType = floor(random(3));
// Randomly choose color index
const colorIndex = floor(random(3));
// For squares and triangles, ensure one side is parallel to x-axis
// by setting rotation to multiples of 90 degrees
const baseRotation =
shapeType === 2 ? random(TWO_PI) : (floor(random(4)) * PI) / 2;
// Animation parameters
const animationDelay = random(0, 10); // Random delay before animation starts
const animationDuration = random(3, 8); // Random duration for each animation cycle
const isActive = random() < 0.3; // Only 30% of shapes are active at a time
const phase = random(TWO_PI); // Random phase for sine wave
// Door-like animation parameters
const doorDirection = floor(random(2)); // 0: horizontal, 1: vertical
const doorSide = floor(random(2)); // 0: left/top, 1: right/bottom
shapes.push({
x: i * cellSize + cellSize / 2,
y: j * cellSize + cellSize / 2,
type: shapeType,
color: colors[colorIndex],
rotation: baseRotation,
baseRotation: baseRotation,
animationDelay: animationDelay,
animationDuration: animationDuration,
isActive: isActive,
phase: phase,
lastAnimationTime: 0,
isAnimating: false,
doorDirection: doorDirection,
doorSide: doorSide,
isClosing: false,
isOpening: false,
});
}
}
}
function draw() {
background(255);
globalTime += 0.03;
// Draw each shape
for (let shape of shapes) {
push();
translate(shape.x, shape.y);
rotate(shape.baseRotation);
// Set fill color
fill(shape.color);
noStroke();
// Check if it's time to start a new animation cycle
if (
shape.isActive &&
globalTime - shape.lastAnimationTime > shape.animationDelay
) {
if (!shape.isAnimating && !shape.isClosing && !shape.isOpening) {
shape.isAnimating = true;
shape.lastAnimationTime = globalTime;
// Start with closing phase
shape.isClosing = true;
shape.isOpening = false;
}
// Handle closing phase
if (shape.isClosing) {
let closeProgress =
(globalTime - shape.lastAnimationTime) / shape.animationDuration;
if (closeProgress > 1) {
closeProgress = 1;
shape.isClosing = false;
shape.isOpening = true;
shape.lastAnimationTime = globalTime;
}
// Draw shape with door-like closing effect
if (shape.type === 0) {
// Square
if (shape.doorDirection === 0) {
// Horizontal door
const width = cellSize * 0.8 * (1 - closeProgress);
if (shape.doorSide === 0) {
// Left side
rect(-cellSize * 0.4 + width / 2, 0, width, cellSize * 0.8);
} else {
// Right side
rect(cellSize * 0.4 - width / 2, 0, width, cellSize * 0.8);
}
} else {
// Vertical door
const height = cellSize * 0.8 * (1 - closeProgress);
if (shape.doorSide === 0) {
// Top side
rect(0, -cellSize * 0.4 + height / 2, cellSize * 0.8, height);
} else {
// Bottom side
rect(0, cellSize * 0.4 - height / 2, cellSize * 0.8, height);
}
}
} else if (shape.type === 1) {
// Triangle
const size = cellSize * 0.8;
if (shape.doorDirection === 0) {
// Horizontal door
const width = size * (1 - closeProgress);
if (shape.doorSide === 0) {
// Left side
const x = -size / 2 + width / 2;
triangle(x, -size / 2, x + width, size / 2, x, size / 2);
} else {
// Right side
const x = size / 2 - width / 2;
triangle(x, -size / 2, x + width, size / 2, x, size / 2);
}
} else {
// Vertical door
const height = size * (1 - closeProgress);
if (shape.doorSide === 0) {
// Top side
const y = -size / 2 + height / 2;
triangle(0, y, size / 2, y + height, -size / 2, y + height);
} else {
// Bottom side
const y = size / 2 - height / 2;
triangle(0, y, size / 2, y + height, -size / 2, y + height);
}
}
} else {
// Circle - use a different approach for circles
const diameter = cellSize * 0.8 * (1 - closeProgress);
ellipse(0, 0, diameter, diameter);
}
}
// Handle opening phase
if (shape.isOpening) {
let openProgress =
(globalTime - shape.lastAnimationTime) / shape.animationDuration;
if (openProgress > 1) {
openProgress = 1;
shape.isOpening = false;
shape.isAnimating = false;
shape.lastAnimationTime = globalTime;
// shape.animationDelay = random(2, 8); // New random delay before next animation
}
// Draw shape with door-like opening effect
if (shape.type === 0) {
// Square
if (shape.doorDirection === 0) {
// Horizontal door
const width = cellSize * 0.8 * openProgress;
if (shape.doorSide === 0) {
// Left side
rect(-cellSize * 0.4 + width / 2, 0, width, cellSize * 0.8);
} else {
// Right side
rect(cellSize * 0.4 - width / 2, 0, width, cellSize * 0.8);
}
} else {
// Vertical door
const height = cellSize * 0.8 * openProgress;
if (shape.doorSide === 0) {
// Top side
rect(0, -cellSize * 0.4 + height / 2, cellSize * 0.8, height);
} else {
// Bottom side
rect(0, cellSize * 0.4 - height / 2, cellSize * 0.8, height);
}
}
} else if (shape.type === 1) {
// Triangle
const size = cellSize * 0.8;
if (shape.doorDirection === 0) {
// Horizontal door
const width = size * openProgress;
if (shape.doorSide === 0) {
// Left side
const x = -size / 2 + width / 2;
triangle(x, -size / 2, x + width, size / 2, x, size / 2);
} else {
// Right side
const x = size / 2 - width / 2;
triangle(x, -size / 2, x + width, size / 2, x, size / 2);
}
} else {
// Vertical door
const height = size * openProgress;
if (shape.doorSide === 0) {
// Top side
const y = -size / 2 + height / 2;
triangle(0, y, size / 2, y + height, -size / 2, y + height);
} else {
// Bottom side
const y = size / 2 - height / 2;
triangle(0, y, size / 2, y + height, -size / 2, y + height);
}
}
} else {
// Circle - use a different approach for circles
const diameter = cellSize * 0.8 * openProgress;
ellipse(0, 0, diameter, diameter);
}
}
} else {
// Draw shape normally when not animating
if (shape.type === 0) {
// Square
rectMode(CENTER);
rect(0, 0, cellSize * 0.8, cellSize * 0.8);
} else if (shape.type === 1) {
// Triangle
const size = cellSize * 0.8;
triangle(0, -size / 2, size / 2, size / 2, -size / 2, size / 2);
} else {
// Circle
ellipse(0, 0, cellSize * 0.8, cellSize * 0.8);
}
}
pop();
}
}
function keyPressed() {
if (key === 's') {
saveGif(`mySketch-${round(new Date().getTime() / 100000)}`, 5);
}
if (key === 'c') {
saveCanvas(`mySketch-${round(new Date().getTime() / 100000)}`, 'jpeg');
}
// Change palette with number keys 1-9
if (key >= '1' && key <= '9') {
const index = parseInt(key) - 1;
if (index < colorPalette.length) {
currentPalette = colorPalette[index].name;
// Reinitialize shapes with new colors
setup();
}
}
// Reset animation with 'r' key
if (key === 'r') {
setup();
}
}

Dat2: Geometric Art - 2
三角関数を多重にかけることで,変わった挙動をするように変更してみた.
本当は point()
関数を使って全然違う絵を描きたかったが,昨日の絵の文脈のまま書いてしまったのと,思ったより見たら面白かったので,今日はこれを作品とする.
ちなみに今日も Cursor
に全振りしてる.
コード
// Grid parameters
const gridSize = 10; // Number of cells in each row/column
let cellSize;
let shapes = [];
let colors = [];
let currentPalette = 'Hokusai'; // You can change this to any palette name from colorPalette.js
let globalTime = 0;
function setup() {
createCanvas((W = min(windowWidth, windowHeight) - 50), W);
cellSize = W / gridSize;
// Select 3 colors from the chosen palette
const palette = colorPalette.find((p) => p.name === currentPalette);
if (palette) {
// Take the first 3 colors from the palette
colors = palette.colors.slice(0, 3);
} else {
// Fallback colors if palette not found
colors = ['#074A59', '#F2C166', '#F28241'];
}
// Initialize shapes
for (let i = 0; i < gridSize; i++) {
for (let j = 0; j < gridSize; j++) {
// Randomly choose shape type (0: square, 1: triangle, 2: circle)
const shapeType = floor(random(3));
// Randomly choose color index
const colorIndex = floor(random(3));
// For squares and triangles, ensure one side is parallel to x-axis
// by setting rotation to multiples of 90 degrees
const baseRotation =
shapeType === 2 ? random(TWO_PI) : (floor(random(4)) * PI) / 2;
// Animation parameters
const animationDelay = random(0, 10); // Random delay before animation starts
const animationDuration = random(3, 8); // Random duration for each animation cycle
const isActive = random() < 0.3; // Only 30% of shapes are active at a time
const phase = random(TWO_PI); // Random phase for sine wave
// Fish fin animation parameters
const finType = floor(random(3)); // 0: dorsal fin, 1: pectoral fin, 2: caudal fin
const finSize = random(0.5, 1.5); // Random size multiplier
const finSpeed = random(0.5, 2); // Random speed multiplier
const finAmplitude = random(0.2, 0.5); // Random amplitude for fin movement
const finFrequency = random(1, 3); // Random frequency for fin movement
// Create the shape object
const shape = {
x: i * cellSize + cellSize / 2,
y: j * cellSize + cellSize / 2,
type: shapeType,
color: colors[colorIndex],
rotation: baseRotation,
baseRotation: baseRotation,
animationDelay: animationDelay,
animationDuration: animationDuration,
isActive: isActive,
phase: phase,
lastAnimationTime: 0,
isAnimating: false,
finType: finType,
finSize: finSize,
finSpeed: finSpeed,
finAmplitude: finAmplitude,
finFrequency: finFrequency,
points: [],
};
// Generate points for the shape
if (shapeType === 0) {
// Square points
const size = cellSize * 0.8;
shape.points = [
{ x: -size / 2, y: -size / 2 },
{ x: size / 2, y: -size / 2 },
{ x: size / 2, y: size / 2 },
{ x: -size / 2, y: size / 2 },
];
} else if (shapeType === 1) {
// Triangle points
const size = cellSize * 0.8;
shape.points = [
{ x: 0, y: -size / 2 },
{ x: size / 2, y: size / 2 },
{ x: -size / 2, y: size / 2 },
];
} else {
// Circle points (approximated with many points)
const radius = cellSize * 0.4;
const numPoints = 20;
for (let p = 0; p < numPoints; p++) {
const angle = (p / numPoints) * TWO_PI;
shape.points.push({
x: radius * cos(angle),
y: radius * sin(angle),
});
}
}
// Add the shape to the shapes array
shapes.push(shape);
}
}
}
function draw() {
background(255);
globalTime += 0.03;
// Draw each shape
for (let shape of shapes) {
push();
translate(shape.x, shape.y);
rotate(shape.baseRotation);
// Set fill color
fill(shape.color);
noStroke();
// Check if it's time to start a new animation cycle
if (
shape.isActive &&
globalTime - shape.lastAnimationTime > shape.animationDelay
) {
if (!shape.isAnimating) {
shape.isAnimating = true;
shape.lastAnimationTime = globalTime;
}
// Draw shape with fish fin-like animation
if (shape.type === 0) {
// Square
drawFinShape(shape);
} else if (shape.type === 1) {
// Triangle
drawFinShape(shape);
} else {
// Circle
drawFinShape(shape);
}
} else {
// Draw shape normally when not animating
if (shape.type === 0) {
// Square
beginShape();
for (let point of shape.points) {
vertex(point.x, point.y);
}
endShape(CLOSE);
} else if (shape.type === 1) {
// Triangle
beginShape();
for (let point of shape.points) {
vertex(point.x, point.y);
}
endShape(CLOSE);
} else {
// Circle
beginShape();
for (let point of shape.points) {
vertex(point.x, point.y);
}
endShape(CLOSE);
}
}
pop();
}
}
function drawFinShape(shape) {
// Calculate time-based animation
const t = globalTime * shape.finSpeed;
// Draw the shape with fin-like movement
beginShape();
if (shape.type === 0) {
// Square with fin movement
for (let i = 0; i < shape.points.length; i++) {
const point = shape.points[i];
let dx = 0;
let dy = 0;
// Apply different fin movements based on fin type
if (shape.finType === 0) {
// Dorsal fin (top edge)
if (i === 0 || i === 1) {
const wave =
sin(t * shape.finFrequency + shape.phase) *
cos(t * shape.finFrequency * 0.5) *
shape.finAmplitude *
cellSize;
dy = wave;
}
} else if (shape.finType === 1) {
// Pectoral fin (side edges)
if (i === 0 || i === 3) {
const wave =
sin(t * shape.finFrequency + shape.phase) *
cos(t * shape.finFrequency * 0.7) *
shape.finAmplitude *
cellSize;
dx = wave;
}
} else {
// Caudal fin (all points with varying intensity)
const intensity = map(i, 0, shape.points.length - 1, 0.2, 1);
const wave =
sin(t * shape.finFrequency + shape.phase + i * 0.5) *
cos(t * shape.finFrequency * 0.3) *
shape.finAmplitude *
cellSize *
intensity;
dx = wave * cos((i * PI) / 2);
dy = wave * sin((i * PI) / 2);
}
vertex(point.x + dx, point.y + dy);
}
} else if (shape.type === 1) {
// Triangle with fin movement
for (let i = 0; i < shape.points.length; i++) {
const point = shape.points[i];
let dx = 0;
let dy = 0;
// Apply different fin movements based on fin type
if (shape.finType === 0) {
// Dorsal fin (top point)
if (i === 0) {
const wave =
sin(t * shape.finFrequency + shape.phase) *
cos(t * shape.finFrequency * 0.5) *
shape.finAmplitude *
cellSize;
dy = wave;
}
} else if (shape.finType === 1) {
// Pectoral fin (bottom points)
if (i === 1 || i === 2) {
const wave =
sin(t * shape.finFrequency + shape.phase) *
cos(t * shape.finFrequency * 0.7) *
shape.finAmplitude *
cellSize;
dx = wave * (i === 1 ? 1 : -1);
}
} else {
// Caudal fin (all points with varying intensity)
const intensity = map(i, 0, shape.points.length - 1, 0.2, 1);
const wave =
sin(t * shape.finFrequency + shape.phase + i * 0.5) *
cos(t * shape.finFrequency * 0.3) *
shape.finAmplitude *
cellSize *
intensity;
dx = wave * cos((i * PI) / 2);
dy = wave * sin((i * PI) / 2);
}
vertex(point.x + dx, point.y + dy);
}
} else {
// Circle with fin movement
for (let i = 0; i < shape.points.length; i++) {
const point = shape.points[i];
let dx = 0;
let dy = 0;
// Apply different fin movements based on fin type
if (shape.finType === 0) {
// Dorsal fin (top half)
if (point.y < 0) {
const wave =
sin(t * shape.finFrequency + shape.phase) *
cos(t * shape.finFrequency * 0.5) *
shape.finAmplitude *
cellSize;
dy = wave;
}
} else if (shape.finType === 1) {
// Pectoral fin (left/right sides)
if (abs(point.x) > abs(point.y)) {
const wave =
sin(t * shape.finFrequency + shape.phase) *
cos(t * shape.finFrequency * 0.7) *
shape.finAmplitude *
cellSize;
dx = wave * (point.x > 0 ? 1 : -1);
}
} else {
// Caudal fin (all points with varying intensity)
const angle = atan2(point.y, point.x);
const intensity = map(sin(angle), -1, 1, 0.2, 1);
const wave =
sin(t * shape.finFrequency + shape.phase + i * 0.5) *
cos(t * shape.finFrequency * 0.3) *
shape.finAmplitude *
cellSize *
intensity;
dx = wave * cos(angle);
dy = wave * sin(angle);
}
vertex(point.x + dx, point.y + dy);
}
}
endShape(CLOSE);
}
function keyPressed() {
if (key === 's') {
saveGif(`mySketch-${round(new Date().getTime() / 100000)}`, 5);
}
if (key === 'c') {
saveCanvas(`mySketch-${round(new Date().getTime() / 100000)}`, 'jpeg');
}
// Change palette with number keys 1-9
if (key >= '1' && key <= '9') {
const index = parseInt(key) - 1;
if (index < colorPalette.length) {
currentPalette = colorPalette[index].name;
// Reinitialize shapes with new colors
setup();
}
}
// Reset animation with 'r' key
if (key === 'r') {
setup();
}
}

Day3: Bubble
正直ちょっと不満というか,納得はいっていない.というのも,開始10minくらいでできたものをアップしているから.ただ,完全に不満ではなく,思ったよりは良いものが一瞬で出てきたなと.
これをブラッシュアップしてより良い作品に仕上げられそうに感じている.
コード
// Splatoon-inspired fluid paint simulation
let splatters = [];
let colors = [];
let maxSplatters = 50;
function setup() {
createCanvas(800, 600);
background(240);
// Splatoon-inspired color palette
colors = [
color(240, 28, 132), // Pink
color(0, 232, 254), // Cyan
color(255, 137, 1), // Orange
color(0, 249, 138), // Green
color(190, 92, 255), // Purple
color(255, 221, 0), // Yellow
];
textAlign(CENTER);
textSize(16);
}
function draw() {
// Update and display all splatters
for (let i = 0; i < splatters.length; i++) {
splatters[i].update();
splatters[i].display();
}
// Display instructions
fill(50);
noStroke();
text('Click or drag to create fluid paint splatters!', width / 2, 30);
}
function mouseDragged() {
createSplatter(mouseX, mouseY);
return false; // Prevent default behavior
}
function mousePressed() {
createSplatter(mouseX, mouseY);
}
function createSplatter(x, y) {
// Limit the number of splatters for performance
if (splatters.length >= maxSplatters) {
// Remove the oldest splatter
splatters.shift();
}
let selectedColor = random(colors);
splatters.push(new FluidSplatter(x, y, selectedColor));
}
class FluidSplatter {
constructor(x, y, color) {
this.x = x;
this.y = y;
this.color = color;
this.size = random(40, 120);
this.blobs = [];
this.blobCount = floor(random(5, 15));
// Create main blob
this.mainBlob = {
offsetX: 0,
offsetY: 0,
size: this.size,
noiseScale: random(0.3, 0.8),
noiseOffset: random(100),
};
// Create connected blobs
let mainAngle = random(TWO_PI);
for (let i = 0; i < this.blobCount; i++) {
// Create blobs in a more connected pattern
let angle = mainAngle + random(-PI / 2, PI / 2);
let distance = random(this.size * 0.3, this.size * 0.8);
this.blobs.push({
offsetX: cos(angle) * distance,
offsetY: sin(angle) * distance,
size: random(this.size * 0.3, this.size * 0.7),
noiseScale: random(0.3, 0.8),
noiseOffset: random(100),
connectionWidth: random(this.size * 0.1, this.size * 0.3),
});
// Slightly adjust the main angle for next blob to create a flow
mainAngle += random(-PI / 4, PI / 4);
}
this.splashProgress = 0;
this.splashSpeed = random(0.03, 0.08);
this.splashComplete = false;
// Add some flow properties
this.flowDirection = random(TWO_PI);
this.flowSpeed = random(0.2, 1);
this.flowAmount = 0;
this.maxFlow = random(10, 30);
}
update() {
if (!this.splashComplete) {
// Animate the splash effect
this.splashProgress += this.splashSpeed;
if (this.splashProgress >= 1) {
this.splashComplete = true;
}
} else {
// After splash is complete, add some subtle flow
this.flowAmount += this.flowSpeed;
if (this.flowAmount > this.maxFlow) {
this.flowSpeed = 0;
}
}
}
display() {
push();
// Apply the color with some transparency
let c = this.color;
fill(red(c), green(c), blue(c), 220);
noStroke();
// Draw connections first (underneath)
if (this.splashProgress > 0.3) {
let connectionProgress = map(this.splashProgress, 0.3, 1, 0, 1);
for (let blob of this.blobs) {
// Draw connection between main blob and this blob
this.drawConnection(
this.x,
this.y,
this.x + blob.offsetX * this.splashProgress,
this.y + blob.offsetY * this.splashProgress,
blob.connectionWidth * connectionProgress,
);
}
}
// Draw main blob
let flowOffsetX = cos(this.flowDirection) * this.flowAmount;
let flowOffsetY = sin(this.flowDirection) * this.flowAmount;
this.drawFluidBlob(
this.x + flowOffsetX,
this.y + flowOffsetY,
this.mainBlob.size * this.splashProgress,
this.mainBlob.noiseScale,
this.mainBlob.noiseOffset,
);
// Draw connected blobs
if (this.splashProgress > 0.2) {
let blobProgress = map(this.splashProgress, 0.2, 1, 0, 1);
for (let blob of this.blobs) {
this.drawFluidBlob(
this.x + blob.offsetX * this.splashProgress + flowOffsetX * 0.7,
this.y + blob.offsetY * this.splashProgress + flowOffsetY * 0.7,
blob.size * blobProgress,
blob.noiseScale,
blob.noiseOffset,
);
}
}
pop();
}
drawFluidBlob(x, y, size, noiseScale, noiseOffset) {
push();
translate(x, y);
beginShape();
for (let a = 0; a < TWO_PI; a += 0.1) {
// Use noise to create organic, fluid-like edges
let xoff = map(cos(a), -1, 1, 0, noiseScale);
let yoff = map(sin(a), -1, 1, 0, noiseScale);
let r = map(
noise(xoff + noiseOffset, yoff + noiseOffset, frameCount * 0.01),
0,
1,
size * 0.7,
size,
);
let px = cos(a) * r;
let py = sin(a) * r;
vertex(px, py);
}
endShape(CLOSE);
// Add some smaller circles inside for a more fluid look
for (let i = 0; i < 3; i++) {
let angle = random(TWO_PI);
let distance = random(size * 0.1, size * 0.4);
let smallSize = random(size * 0.1, size * 0.3);
ellipse(
cos(angle) * distance,
sin(angle) * distance,
smallSize,
smallSize,
);
}
pop();
}
drawConnection(x1, y1, x2, y2, width) {
// Draw a smooth connection between two points
push();
// Find the midpoint with some random offset for natural flow
let midX = (x1 + x2) / 2 + random(-width / 2, width / 2);
let midY = (y1 + y2) / 2 + random(-width / 2, width / 2);
// Draw a curved shape to connect the blobs
beginShape();
// Start at first point
vertex(x1, y1);
// Add control points for a natural curve
let controlX1 = x1 + (midX - x1) * 0.5;
let controlY1 = y1 + (midY - y1) * 0.5;
let controlX2 = midX + (x2 - midX) * 0.5;
let controlY2 = midY + (y2 - midY) * 0.5;
// Add points along the curve with some width
for (let t = 0; t <= 1; t += 0.1) {
// Bezier curve calculation
let bx = bezierPoint(x1, controlX1, controlX2, x2, t);
let by = bezierPoint(y1, controlY1, controlY2, y2, t);
// Add perpendicular width
let angle =
atan2(
bezierTangent(y1, controlY1, controlY2, y2, t),
bezierTangent(x1, controlX1, controlX2, x2, t),
) + HALF_PI;
// Width varies along the connection for a more natural look
let currentWidth = width * (1 - abs(t - 0.5) * 1.5);
if (currentWidth > 0) {
vertex(bx + cos(angle) * currentWidth, by + sin(angle) * currentWidth);
}
}
// Go back along the other side
for (let t = 1; t >= 0; t -= 0.1) {
let bx = bezierPoint(x1, controlX1, controlX2, x2, t);
let by = bezierPoint(y1, controlY1, controlY2, y2, t);
let angle =
atan2(
bezierTangent(y1, controlY1, controlY2, y2, t),
bezierTangent(x1, controlX1, controlX2, x2, t),
) + HALF_PI;
let currentWidth = width * (1 - abs(t - 0.5) * 1.5);
if (currentWidth > 0) {
vertex(bx - cos(angle) * currentWidth, by - sin(angle) * currentWidth);
}
}
endShape(CLOSE);
pop();
}
}
function keyPressed() {
if (key === 's') {
saveGif(`mySketch-${round(new Date().getTime() / 100000)}`, 5);
}
if (key === 'c') {
saveCanvas(`mySketch-${round(new Date().getTime() / 100000)}`, 'jpeg');
}
}

Day4: Circle Packing
今日からどんなプロンプトを投げたかも乗せておく
本当は,任意の絵文字を画面の中央に拡大して描画し,それを画像をして認識し,その画像内を円充填したかったが,何度やっても loadPixles()
後の pixels
配列を観ても,絵文字の部分がすべて 255
のままだったため,無理だった.途中からガチで自分自身でデバッグして無理だったので,自分の能力不足のため,どっかでリベンジする.
ということで,違う円充填を描いた.
キャンバス内に quad
を用いてランダムな配置とサイズで図形を描き,その内部を4色の円で充填させたもの.
- 一回絵文字からやるのは忘れて,シンプルに円充填をしてみて欲しい
- 円充填がどういうものかそもそも知っていますか?
- デフォルメした牛を横から見た姿の外枠だけを線で描き,その内部を円充填できる?
- 流石に牛の外枠は難しかったと思うので,ランダムな四角形(rect ではなく,quadを使う)をいくつか画面上にランダム配置させ,その内部を円充填して
- isInsideQuad() メソッドの頂点判定処理を書いて
- 色はデザインや色彩のノウハウに基づいた3or4色にして
コード
let W; // キャンバスのサイズ
let circles = []; // 円の配列
let gridSize = 10; // グリッドのサイズ
let maxAttempts = 1000; // 円を配置する試行回数
let quads = [];
let maxQuads = 5; // 四角形の数
let colors = [];
function setup() {
W = min(windowWidth, windowHeight) - 100;
createCanvas(W, W);
background(255);
noStroke();
drawCowOutline();
noLoop();
colors = [
color(41, 128, 185), // ベースカラー(青)
color(39, 174, 96), // アナログスキーム(青緑)
color(142, 68, 173), // アナログスキーム(紫)
color(230, 126, 34), // アクセントカラー(オレンジ)
];
// ランダムな四角形を生成
for (let i = 0; i < maxQuads; i++) {
let x1 = random(width);
let y1 = random(height);
let x2 = random(width);
let y2 = random(height);
let x3 = random(width);
let y3 = random(height);
let x4 = random(width);
let y4 = random(height);
quads.push({ x1, y1, x2, y2, x3, y3, x4, y4 });
}
// 円を配置
for (let i = 0; i < maxAttempts; i++) {
let x = random(width);
let y = random(height);
let r = random(5, 20);
let valid = true;
// 既存の円と重ならないかチェック
for (let circle of circles) {
let d = dist(x, y, circle.x, circle.y);
if (d < r + circle.r) {
valid = false;
break;
}
}
// 四角形の内部にあるかチェック
if (valid && isInsideQuad(x, y)) {
circles.push({ x, y, r, color: colors[floor(random(colors.length))] });
}
}
}
function draw() {
background(255);
drawCowOutline();
// 四角形を描画
console.log(quads);
for (let item of quads) {
quad(
item.x1,
item.y1,
item.x2,
item.y2,
item.x3,
item.y3,
item.x4,
item.y4,
);
}
push();
for (let circle of circles) {
fill(circle.color);
ellipse(circle.x, circle.y, circle.r * 2);
}
pop();
}
// 円のサイズを決定する関数
function getCircleSize(x, y) {
let minSize = 2;
let maxSize = gridSize;
// ここで円の大きさを決定するロジックを追加
return random(minSize, maxSize);
}
// 点が四角形の内部にあるかチェックする関数
function isInsideQuad(x, y) {
let inside = false;
for (let quad of quads) {
// 四角形の頂点を配列に格納
let vertices = [
{ x: quad.x1, y: quad.y1 },
{ x: quad.x2, y: quad.y2 },
{ x: quad.x3, y: quad.y3 },
{ x: quad.x4, y: quad.y4 },
];
// Ray Casting Algorithm
let j = vertices.length - 1;
for (let i = 0; i < vertices.length; i++) {
let xi = vertices[i].x;
let yi = vertices[i].y;
let xj = vertices[j].x;
let yj = vertices[j].y;
// 点が辺の上にある場合は内部と判定
if (xi === xj && yi === yj) continue;
if (xi === x && yi === y) return true;
// 辺と水平線の交差判定
let intersect =
yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;
if (intersect) inside = !inside;
j = i;
}
if (inside) return true;
}
return false;
}
function drawCowOutline() {
beginShape();
// 牛の頭部
vertex(100, 100);
vertex(150, 50);
vertex(200, 100);
// 牛の胴体
vertex(250, 150);
vertex(300, 200);
vertex(250, 250);
// 牛の後ろ足
vertex(200, 300);
vertex(150, 250);
// 牛の前足
vertex(100, 200);
vertex(50, 150);
endShape(CLOSE);
}
function keyPressed() {
if (key === 'c') {
saveCanvas('myCanvas', 'png');
}
}

Day5: simple animation with trigonometric functions
アイディアそのものが全く浮かばなかったので,ちょっと置きに行った作品.
困ったら誰かの作品を模倣するところから言っても良いかもなぁ.
- ドットを使ってアニメーションを描いてください.三角関数を複数組み合わせて,美しい動きをして欲しい
- あまりにも単調な動きのため,もっと三角関数を複数回組み合わせて動きも複雑化して
- もっと複雑な動きをして欲しい.ダイナミックさも付け加えて
- elilpseを絵文字にしたのですが,進行方向に向かって角度を自動で変更して欲しい
コード
let time = 0;
const numDots = 80;
let baseRadius;
// 動的なパラメータ
let pulse = 0;
let rotationSpeed = 0.3;
let expansionFactor = 1;
// 前フレームの位置を保存する配列
let prevPositions = [];
function setup() {
createCanvas((W = min(windowWidth, windowHeight) - 300), W);
baseRadius = W / 4.5;
textSize(W / 32);
textAlign(CENTER, CENTER);
}
function draw() {
background(255);
// 動的なパラメータの更新
pulse = sin(time * 0.5) * 0.2 + 1; // 0.8から1.2の間で脈動
rotationSpeed = 0.3 + sin(time * 0.2) * 0.1; // 回転速度の変化
expansionFactor = 1 + sin(time * 0.3) * 0.3; // 全体の拡大縮小
push();
translate(width / 2, height / 2);
// 現在の位置を保存する配列
let currentPositions = [];
for (let i = 0; i < numDots; i++) {
const angle = (i / numDots) * TWO_PI;
// より複雑な半径の変化
const radiusVariation =
sin(time + angle * 3) * 30 * pulse +
cos(time * 0.7 + angle * 2) * 20 +
sin(time * 1.3 + angle * 4) * 15 +
cos(time * 0.4 + angle * 5) * 10 +
sin(time * 2.1 + angle * 6) * 8;
const radius = (baseRadius + radiusVariation) * expansionFactor;
// より複雑な位置計算
const xOffset =
sin(time * 0.5 + angle * 2) * 40 * pulse +
cos(time * 0.3 + angle * 3) * 30 +
sin(time * 1.2 + angle * 4) * 20 +
cos(time * 0.8 + angle * 5) * 15;
const yOffset =
cos(time * 0.7 + angle * 2) * 40 * pulse +
sin(time * 0.4 + angle * 3) * 30 +
cos(time * 1.5 + angle * 4) * 20 +
sin(time * 0.9 + angle * 5) * 15;
const x = cos(angle + time * rotationSpeed) * radius + xOffset;
const y = sin(angle + time * rotationSpeed) * radius + yOffset;
// 現在の位置を保存
currentPositions[i] = { x, y };
// より複雑な色の変化
const hue =
(i * 2 +
time * 30 +
sin(time + i * 0.1) * 30 +
cos(time * 0.5 + i * 0.2) * 20) %
360;
const saturation =
70 + sin(time * 2 + i * 0.05) * 30 + cos(time * 1.2 + i * 0.1) * 15;
const brightness =
80 + cos(time * 1.5 + i * 0.03) * 20 + sin(time * 0.8 + i * 0.07) * 15;
fill(hue, saturation, brightness);
noStroke();
// 回転角度の計算
let rotation = 0;
if (prevPositions[i]) {
const dx = x - prevPositions[i].x;
const dy = y - prevPositions[i].y;
rotation = atan2(dy, dx);
}
// はさみの絵文字を回転させて描画
push();
translate(x, y);
rotate(rotation);
text('✄', 0, 0);
pop();
}
// 前フレームの位置を更新
prevPositions = currentPositions;
pop();
time += 0.03;
}
function keyPressed() {
if (key === 's') {
saveGif(`mySketch-${round(new Date().getTime() / 100000)}`, 4);
}
if (key === 'c') {
saveCanvas(`mySketch-${round(new Date().getTime() / 100000)}`, 'jpeg');
}
}

Day6: Dotts Image
ドットによる点描のアニメーションや画像は地味に好きなのでなにかないかなーということで書いてみた.
改良の余地はあるが,眠すぎるのでこれが今の実力ということで,明日以降の俺に託す.
- p5jsを用いて点描でなにか作品を作ってみたい.デフォルメした犬のシュナウザーを横から見た姿を描ける?
- 添付画像のように割といい感じのコードでした!これをもっとシンプルに,シルエットのようにして欲しい
- おしいが,手足が単なる長方形になってしまっているので,もう少し犬の足に近づけて.顔も,斜め左下方向への長方形で,角丸に
- コードが途中で途切れたので続けて
途中経過のものもすべて添付
コード
function setup() {
createCanvas(600, 400);
background(240);
noLoop();
}
function draw() {
let silhouetteColor = color(50); // シルエットの色 (濃いグレー)
// 点描の密度や点の大きさを調整するパラメータ
let densityFactor = 1.3; // 点の密度を少し上げる
let dotMinSize = 2;
let dotMaxSize = 5; // 少し点の最大サイズを上げてみる
// 元の各パーツの密度目安
let baseDensity = 1000;
// --- シュナウザーのシルエット描画 ---
// 体 (少し調整)
drawDottedEllipse(
width / 2,
height / 2 + 35,
200,
130,
silhouetteColor,
baseDensity * 1.5 * densityFactor,
dotMinSize,
dotMaxSize,
);
// 顔 (斜め左下方向への角丸長方形)
push();
translate(width / 2 - 105, height / 2 - 30); // 顔の中心位置を調整
rotate(radians(-25)); // 回転角度を調整
// drawDottedRoundedRect のx,yは回転後のローカル座標なので0,0でOK
drawDottedRoundedRect(
0,
0,
90,
60,
15,
silhouetteColor,
baseDensity * 0.9 * densityFactor,
dotMinSize,
dotMaxSize,
);
pop();
// マズル (顔と一体化するように位置調整)
// 顔の回転と位置に合わせて調整が必要
// push/popとtranslate/rotateで顔と一緒に描画する方が連動しやすいが、ここでは個別に配置
push();
translate(width / 2 - 130, height / 2 + 5); // マズルの基準点
rotate(radians(-10)); // マズルも少し傾ける
drawDottedRectangle(
0,
0,
70,
40,
silhouetteColor,
baseDensity * 0.7 * densityFactor,
dotMinSize,
dotMaxSize,
);
pop();
// 耳 (垂れ耳風シルエット)
push();
translate(width / 2 - 90, height / 2 - 65); // 位置調整
rotate(radians(-30)); // 角度調整
drawDottedEllipse(
0,
0,
45,
75,
silhouetteColor,
baseDensity * 0.5 * densityFactor,
dotMinSize,
dotMaxSize,
);
pop();
// 前足1
drawDottedLeg(
width / 2 - 55,
height / 2 + 70,
35,
55,
28,
48,
10,
silhouetteColor,
baseDensity * 0.45 * densityFactor,
dotMinSize,
dotMaxSize,
);
// 前足2
drawDottedLeg(
width / 2 - 20,
height / 2 + 70,
35,
55,
28,
48,
-5,
silhouetteColor,
baseDensity * 0.45 * densityFactor,
dotMinSize,
dotMaxSize,
);
// 後ろ足1 (太もも部分がより角度がつくように)
drawDottedLeg(
width / 2 + 55,
height / 2 + 60,
45,
65,
35,
55,
-35,
silhouetteColor,
baseDensity * 0.5 * densityFactor,
dotMinSize,
dotMaxSize,
);
// 後ろ足2 (少し奥にあるように見せるため、サイズや位置を微調整)
drawDottedLeg(
width / 2 + 85,
height / 2 + 65,
42,
62,
33,
53,
-45,
silhouetteColor,
baseDensity * 0.45 * densityFactor,
dotMinSize,
dotMaxSize,
);
// 尻尾
push();
translate(width / 2 + 105, height / 2 + 15); // 位置調整
rotate(radians(30)); // 角度調整
drawDottedEllipse(
0,
0,
25,
45,
silhouetteColor,
baseDensity * 0.3 * densityFactor,
dotMinSize,
dotMaxSize,
);
pop();
}
// 指定された範囲に点を描画するヘルパー関数 (楕円)
function drawDottedEllipse(x, y, w, h, col, numPoints, minSize, maxSize) {
// 楕円の中心をx,yとする
for (let i = 0; i < numPoints; i++) {
let angle = random(TWO_PI);
// 楕円内部に均等に分布させるための工夫
let rScale = sqrt(random()); // sqrt(random()) にすると中心付近の密度が下がり、より均一に近くなる
let rX = (w / 2) * rScale;
let rY = (h / 2) * rScale;
let dotX = x + cos(angle) * rX;
let dotY = y + sin(angle) * rY;
let dSize = random(minSize, maxSize);
fill(col);
noStroke();
ellipse(dotX, dotY, dSize, dSize);
}
}
// 指定された範囲に点を描画するヘルパー関数 (四角形)
function drawDottedRectangle(x, y, w, h, col, numPoints, minSize, maxSize) {
// 四角形の左上をx,yとする
for (let i = 0; i < numPoints; i++) {
let dotX = random(x, x + w);
let dotY = random(y, y + h);
let dSize = random(minSize, maxSize);
fill(col);
noStroke();
ellipse(dotX, dotY, dSize, dSize);
}
}
// 指定された範囲に点を描画するヘルパー関数 (角丸四角形)
// rectX, rectY は現在の描画コンテキストでの角丸四角形の左上の座標(通常は0,0 nếu translate後)
function drawDottedRoundedRect(
rectX,
rectY,
w,
h,
r,
col,
numPoints,
minSize,
maxSize,
) {
r = min(r, w / 2, h / 2); // 半径が幅や高さの半分を超えないように調整
let drawnPoints = 0;
let maxAttempts = numPoints * 10; // 無限ループを避けるための最大試行回数
for (let i = 0; i < maxAttempts && drawnPoints < numPoints; i++) {
// 角丸四角形のバウンディングボックス内でランダムな点を生成 (ローカル座標)
let px = random(w); // rectX から rectX + w ではなく、ローカルの 0 から w
let py = random(h); // rectY から rectY + h ではなく、ローカルの 0 から h
let inside = false;
// 点が角丸四角形の内部にあるか判定
if (px > r && px < w - r) {
// 中央の垂直帯内部(yは全長OK)
inside = true;
} else if (py > r && py < h - r) {
// 中央の水平帯内部(xは全長OK)
inside = true;
} else {
// 角の領域にある可能性
if (px <= r && py <= r) {
// 左上角
if (dist(px, py, r, r) <= r) inside = true;
} else if (px >= w - r && py <= r) {
// 右上角
if (dist(px, py, w - r, r) <= r) inside = true;
} else if (px <= r && py >= h - r) {
// 左下角
if (dist(px, py, r, h - r) <= r) inside = true;
} else if (px >= w - r && py >= h - r) {
// 右下角
if (dist(px, py, w - r, h - r) <= r) inside = true;
}
}
if (inside) {
let dSize = random(minSize, maxSize);
fill(col);
noStroke();
// 描画はローカル座標 (px, py) で行う。`translate()` で既に位置決めされている前提。
ellipse(rectX + px, rectY + py, dSize, dSize); // 描画は rectX, rectY を基準にオフセット
drawnPoints++;
}
}
}
// 改良された足の描画関数 (2つの楕円セグメントで構成)
// baseX, baseY: 足の付け根の回転軸となる位置
// upperW, upperH: 上部セグメントの幅と高さ
// lowerW, lowerH: 下部セグメントの幅と高さ
// legAngle: 足全体の基準線からの傾き (度数法)
function drawDottedLeg(
baseX,
baseY,
upperW,
upperH,
lowerW,
lowerH,
legAngle,
col,
numPoints,
minSize,
maxSize,
) {
push();
translate(baseX, baseY); // 足の付け根に原点を移動
rotate(radians(legAngle)); // 足全体を指定された角度だけ回転
// 上部セグメント (例: 太もも)
// 回転軸(0,0)から下に伸びるように、中心を(0, upperH/2)に配置
drawDottedEllipse(
0,
upperH / 2,
upperW,
upperH,
col,
floor(numPoints * 0.6),
minSize,
maxSize,
);
// 下部セグメント (例: すね + 足先)
// 上部セグメントの終端あたりから始まるように配置
// Y座標のオフセット: 上部セグメントの高さ + 下部セグメントの高さの半分
// 少し重なるように upperH * 0.85 などで調整可能
let lowerSegmentCenterY = upperH * 0.9 + (lowerH / 2) * 0.8; // Y位置を微調整して接続を自然に
let lowerSegmentOffsetX = upperW * 0.05; // X位置を微調整して「く」の字に(お好みで)
drawDottedEllipse(
lowerSegmentOffsetX,
lowerSegmentCenterY,
lowerW,
lowerH,
col,
floor(numPoints * 0.4),
minSize,
maxSize,
);
pop();
}
// キーボード操作
function keyPressed() {
const timestamp = round(new Date().getTime() / 100000);
saveCanvas(`screenshot-${timestamp}`, 'jpeg');
}

Day7: easing animation
プロンプトは以前のものがなくなってしまったので今回はなしで.
Okazz さんの様なアニメーションをしてみたかったので,ちょっとコードはパクった.
といってもまだ不完全さは残る.
コード
const ANIMATION_SPAN = 200; // アニメーションの長さ
const GRID_SIZE = 10; // グリッドのサイズ
const SCALE_FACTOR = 0; // 最小サイズを0に設定
const DELAY_FACTOR = 5; // 隣接する正方形間の遅延
// キャンバス設定
let canvasSize;
let cellSize;
const rectangles = [];
function setup() {
canvasSize = min(windowWidth, windowHeight) - 200;
createCanvas(canvasSize, canvasSize);
noStroke();
rectMode(CENTER);
// セルのサイズを計算
cellSize = canvasSize / GRID_SIZE;
// グリッド状に正方形を配置
for (let i = 0; i < GRID_SIZE; i++) {
for (let j = 0; j < GRID_SIZE; j++) {
// 格子状に配置
const x = i * cellSize + cellSize / 2;
const y = j * cellSize + cellSize / 2;
// 位置による遅延を設定 (左上から右下へ)
const delay = (i + j) * DELAY_FACTOR;
rectangles.push(new AnimatedRectangle(x, y, cellSize, cellSize, delay));
}
}
}
function draw() {
background(255);
// すべての長方形を更新して描画
for (const rect of rectangles) {
rect.update();
rect.display();
}
}
class AnimatedRectangle {
constructor(x, y, width, height, delay) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.delay = delay;
this.timer = 0;
this.direction = 1; // 1: 増加, -1: 減少
this.animationProgress = 0;
}
update() {
// 遅延後にアニメーションを開始
if (frameCount < this.delay) {
this.animationProgress = 1; // 最初は最大サイズ
return;
}
// タイマーを更新
this.timer += this.direction;
// アニメーション方向を切り替え
if (this.timer >= ANIMATION_SPAN) {
this.direction = -1;
} else if (this.timer <= 0) {
this.direction = 1;
}
// イージング関数を適用してアニメーション進捗を計算
const normalizedTime =
this.direction === -1
? norm(this.timer, 0, ANIMATION_SPAN)
: norm(this.timer, ANIMATION_SPAN, 0);
this.animationProgress =
this.direction === 1
? easeInOutQuint(normalizedTime)
: easeInOutQuint(1 - normalizedTime);
}
display() {
// サイズをアニメーション進捗に基づいて補間
const currentWidth =
lerp(SCALE_FACTOR, 1, this.animationProgress) * this.width;
const currentHeight =
lerp(SCALE_FACTOR, 1, this.animationProgress) * this.height;
fill(0);
rect(this.x, this.y, currentWidth, currentHeight);
}
}
// イージング関数(5次元の入出力補間)
function easeInOutQuint(x) {
return x < 0.5 ? 16 * x * x * x * x * x : 1 - pow(-2 * x + 2, 5) / 2;
}
// キーボード操作
function keyPressed() {
if (key === 's') {
saveGif(`animation-${round(new Date().getTime() / 100000)}`, 8);
} else if (key === 'c') {
saveCanvas(`screenshot-${round(new Date().getTime() / 100000)}`, 'jpeg');
}
}

Day8: Colorful interactive and animation
最近は Claude Code で書かせることが増えてきた.𝕏 からパクったプロンプトをそのまま Claude Code で実行した形.
Create an amazing animation multicolor and interactive using p5js
- マウス操作に応じて,背景のドットの動きが変化する(左右のクリックで若干違いもある)
- あとは,
s,c, r, p
キーごとに違う挙動が設定されていたりと
結果としては,自分の普段書くクセなども盛り込んだコードになっており,なかなかやるなと感心している.
コードは長いのでこちらをご参照

Day9: Pop Image
まさに小さな子どもの服やおもちゃなどの柄に使われそうな作品が出来上がった.今回も Claude Code に書かせてみた.ちなみにプロンプトは2つで微調整をした形.
- Create an pop and multicolor image using p5js
- Textbooks are not required. Place the objects in a grid. The shape and size of the objects are random
コード
let W;
let popColors;
let grid;
let shapes = [];
function setup() {
W = min(windowWidth, windowHeight) - 50;
createCanvas(W, W);
// Pop art inspired color palette
popColors = [
'#FF006E', // Hot Pink
'#FFBE0B', // Electric Yellow
'#FB5607', // Bright Orange
'#8338EC', // Electric Purple
'#3A86FF', // Bright Blue
'#06FFA5', // Electric Green
'#FF4081', // Pink
'#00E676', // Green
'#FF5722', // Deep Orange
'#E91E63', // Pink
'#9C27B0', // Purple
'#2196F3', // Blue
'#4CAF50', // Green
'#FF9800', // Orange
'#F44336', // Red
'#FFEB3B', // Yellow
'#00BCD4', // Cyan
'#795548', // Brown
];
noLoop(); // Static image
createPopArtComposition();
}
function draw() {
// Create clean background
background(255);
// Draw geometric pop art elements in grid
drawPopArtShapes();
}
function createPopArtComposition() {
// Create grid of pop art elements with random shapes and sizes
let cols = 8;
let rows = 8;
let cellW = W / cols;
let cellH = W / rows;
for (let i = 0; i < cols; i++) {
for (let j = 0; j < rows; j++) {
let x = i * cellW;
let y = j * cellH;
// Random size variations
let sizeVariation = random(0.3, 0.9);
let w = cellW * sizeVariation;
let h = cellH * sizeVariation;
// Sometimes make shapes very different aspect ratios
if (random() < 0.3) {
w = cellW * random(0.2, 0.9);
h = cellH * random(0.2, 0.9);
}
shapes.push({
x: x + cellW / 2,
y: y + cellH / 2,
w: w,
h: h,
type: random([
'circle',
'rect',
'triangle',
'star',
'hexagon',
'diamond',
'pentagon',
]),
color: random(popColors),
rotation: random(TWO_PI),
});
}
}
}
function drawPopArtShapes() {
for (let shape of shapes) {
push();
translate(shape.x, shape.y);
rotate(shape.rotation);
// Drop shadow effect
fill(0, 100);
noStroke();
drawShape(shape.type, 5, 5, shape.w, shape.h);
// Main shape
fill(shape.color);
stroke(0);
strokeWeight(4);
drawShape(shape.type, 0, 0, shape.w, shape.h);
// Highlight effect
fill(255, 150);
noStroke();
drawShape(shape.type, -2, -2, shape.w * 0.3, shape.h * 0.3);
pop();
}
}
function drawShape(type, x, y, w, h) {
push();
translate(x, y);
switch (type) {
case 'circle':
ellipse(0, 0, w, h);
break;
case 'rect':
rectMode(CENTER);
rect(0, 0, w, h, 10);
break;
case 'triangle':
triangle(-w / 2, h / 2, w / 2, h / 2, 0, -h / 2);
break;
case 'star':
drawStar(0, 0, w / 4, w / 2, 5);
break;
case 'hexagon':
drawHexagon(0, 0, w / 2);
break;
case 'diamond':
drawDiamond(0, 0, w / 2, h / 2);
break;
case 'pentagon':
drawPentagon(0, 0, w / 2);
break;
}
pop();
}
function drawStar(x, y, radius1, radius2, npoints) {
let angle = TWO_PI / npoints;
let halfAngle = angle / 2.0;
beginShape();
for (let a = 0; a < TWO_PI; a += angle) {
let sx = x + cos(a) * radius2;
let sy = y + sin(a) * radius2;
vertex(sx, sy);
sx = x + cos(a + halfAngle) * radius1;
sy = y + sin(a + halfAngle) * radius1;
vertex(sx, sy);
}
endShape(CLOSE);
}
function drawHexagon(x, y, radius) {
beginShape();
for (let i = 0; i < 6; i++) {
let angle = (i * TWO_PI) / 6;
let sx = x + cos(angle) * radius;
let sy = y + sin(angle) * radius;
vertex(sx, sy);
}
endShape(CLOSE);
}
function drawDiamond(x, y, w, h) {
beginShape();
vertex(x, y - h);
vertex(x + w, y);
vertex(x, y + h);
vertex(x - w, y);
endShape(CLOSE);
}
function drawPentagon(x, y, radius) {
beginShape();
for (let i = 0; i < 5; i++) {
let angle = (i * TWO_PI) / 5 - PI / 2;
let sx = x + cos(angle) * radius;
let sy = y + sin(angle) * radius;
vertex(sx, sy);
}
endShape(CLOSE);
}
function mousePressed() {
// Regenerate the composition
shapes = [];
createPopArtComposition();
redraw();
}
function keyPressed() {
if (key === 'c') {
saveCanvas(`pop-art-${round(new Date().getTime() / 100000)}`, 'jpeg');
}
if (key === 'r') {
// Regenerate composition
shapes = [];
createPopArtComposition();
redraw();
}
}

Day10: Symbiosis of digital(images) and reality
タイトル通りだが,やってみたかったというか,個人的に追っているものの一つに.デジタルアートと現実を上手く同居したような作品を考えていて,その一環の一つ.
指定した画像をピクセルで管理し,デジタルでまばらなピクセルと1ピクセルできっちり指定した画像を表示することで現実っぽい描写.
今回の作品は OpenProcessing にも投稿済み
コード
let img;
let pixelData = [];
let canvasSize;
let isVertical = false;
function setup() {
canvasSize = min(windowWidth, windowHeight) - 200;
let canvas = createCanvas(canvasSize, canvasSize);
canvas.parent('canvas-container');
noLoop();
background(255);
textAlign(CENTER, CENTER);
textSize(16);
fill(100);
text('Upload an image to begin', width / 2, height / 2);
let imageInput = select('#imageInput');
imageInput.changed(handleImageUpload);
}
function draw() {
if (!img || !img.width) {
return;
}
background(255);
drawPixelatedImage();
}
function handleImageUpload() {
let input = select('#imageInput').elt;
if (input.files && input.files[0]) {
let file = input.files[0];
let reader = new FileReader();
reader.onload = function (e) {
img = loadImage(e.target.result, imageLoaded);
};
reader.readAsDataURL(file);
}
}
function imageLoaded() {
if (!img) return;
isVertical = img.height > img.width;
processImagePixels();
redraw();
}
function processImagePixels() {
pixelData = [];
img.loadPixels();
let scaleFactor = min(canvasSize / img.width, canvasSize / img.height);
let displayWidth = img.width * scaleFactor;
let displayHeight = img.height * scaleFactor;
let offsetX = (canvasSize - displayWidth) / 2;
let offsetY = (canvasSize - displayHeight) / 2;
let minRes = 1;
let maxRes = 30;
if (isVertical) {
for (let y = 0; y < displayHeight; ) {
let progress = 1 - y / displayHeight;
let pixelSize =
progress > 0.7 ? minRes : lerp(maxRes, minRes, progress / 0.7);
for (let x = 0; x < displayWidth; x += pixelSize) {
let sourceX = int(x / scaleFactor);
let sourceY = int(y / scaleFactor);
if (
sourceX >= 0 &&
sourceX < img.width &&
sourceY >= 0 &&
sourceY < img.height
) {
let index = (sourceY * img.width + sourceX) * 4;
let r = img.pixels[index];
let g = img.pixels[index + 1];
let b = img.pixels[index + 2];
pixelData.push({
x: x + offsetX,
y: y + offsetY,
color: color(r, g, b),
size: pixelSize,
progress: progress,
});
}
}
y += pixelSize;
}
} else {
for (let x = 0; x < displayWidth; ) {
let progress = 1 - x / displayWidth;
let pixelSize =
progress > 0.7 ? minRes : lerp(maxRes, minRes, progress / 0.7);
for (let y = 0; y < displayHeight; y += pixelSize) {
let sourceX = int(x / scaleFactor);
let sourceY = int(y / scaleFactor);
if (
sourceX >= 0 &&
sourceX < img.width &&
sourceY >= 0 &&
sourceY < img.height
) {
let index = (sourceY * img.width + sourceX) * 4;
let r = img.pixels[index];
let g = img.pixels[index + 1];
let b = img.pixels[index + 2];
pixelData.push({
x: x + offsetX,
y: y + offsetY,
color: color(r, g, b),
size: pixelSize,
progress: progress,
});
}
}
x += pixelSize;
}
}
}
function drawPixelatedImage() {
if (pixelData.length === 0) return;
for (let pixel of pixelData) {
let progress = pixel.progress;
let pixelColor = pixel.color;
if (
brightness(pixelColor) > 90 &&
red(pixelColor) > 240 &&
green(pixelColor) > 240 &&
blue(pixelColor) > 240
) {
pixelColor = color(255, 255, 255);
}
if (progress > 0.7) {
fill(pixelColor);
noStroke();
rect(pixel.x, pixel.y, pixel.size, pixel.size);
} else {
let normalizedProgress = progress / 0.7;
let fillAlpha = map(normalizedProgress, 0, 1, 0, 255);
fillAlpha = constrain(fillAlpha, 0, 255);
let strokeOpacity = map(normalizedProgress, 0, 1, 255, 100);
let strokeColor = lerpColor(color(0), pixelColor, normalizedProgress);
strokeColor = color(
red(strokeColor),
green(strokeColor),
blue(strokeColor),
strokeOpacity,
);
stroke(strokeColor);
strokeWeight(map(normalizedProgress, 0, 1, 1, 0.3));
fill(red(pixelColor), green(pixelColor), blue(pixelColor), fillAlpha);
drawGradualRect(
pixel.x,
pixel.y,
pixel.size,
pixel.size,
normalizedProgress,
);
}
}
}
function drawGradualRect(x, y, w, h, progress) {
let maxJitter = 3;
let jitterAmount = map(progress, 0, 1, maxJitter, 0);
if (progress < 0.9) {
let corners = [
{
x: x + random(-jitterAmount, jitterAmount),
y: y + random(-jitterAmount, jitterAmount),
},
{
x: x + w + random(-jitterAmount, jitterAmount),
y: y + random(-jitterAmount, jitterAmount),
},
{
x: x + w + random(-jitterAmount, jitterAmount),
y: y + h + random(-jitterAmount, jitterAmount),
},
{
x: x + random(-jitterAmount, jitterAmount),
y: y + h + random(-jitterAmount, jitterAmount),
},
];
beginShape();
for (let corner of corners) {
vertex(corner.x, corner.y);
}
endShape(CLOSE);
} else {
rect(x, y, w, h);
}
}
function keyPressed() {
if (key === 's') {
saveGif(`photo-pixel-gradient-${round(new Date().getTime() / 100000)}`, 1);
}
if (key === 'c') {
saveCanvas(
`photo-pixel-gradient-${round(new Date().getTime() / 100000)}`,
'jpeg',
);
}
}