- Generative Coding
- 久保田晃弘(多摩美術大学)
- Creative Coding と Generative Art を分離する
- Code (and) Art
- Generative Art:コードの実行結果としての視覚芸術
- Algorithmic Music:コードの実行結果としての音楽
- Code Poetry:プログラムコードで詩を書く
- Creative Coding:プログラムを書いて創造的な作品を生み出す
- 創造的(Creative)であるとはどういうことか?
- Creative Programmer / Wouter Groeneveld(2023)
- プログラミングは創造的な行為です。これらのテクニックは、創造力を最大限に引き出してソフトウェアを改善し、ソフトウェア作成の満足度を高めるのに役立ちます。
- クリエイティブプログラマー/ウーター・グローネフェルド
- 結論:プログラミングはあたりまえの行為である
- Creativity 批判
- Against Creativity / Oli Mould(2020)
- Generative Coding
- コードを書くことの意味を再考する
- 遺伝子組み換え(Creative Coding)と園芸(Generative Art)
- Coding が面白いのか Generationが面白いのか?
- Creative Coder なのか Generative Artist なのか?
- 人間ががんばってコードを書けば書くほどGenerative Artから遠ざかる?
- 人間がなるべく何もしない方がいいのかもしれない?
- 批判理論(Critical Theory)と解釈学(Hermeneutics)の必要性
- Critical Code Studies(2020)
- Live Coding: A User's Manual(2022)
- ジェネラティブ・アート/マット・ピアソン(2012)
- 監訳者のことば(冒頭部分)
- コンピュータを用いることは、Generative Artの必要条件ではない
- Generative Art Timeline
- 生成音楽ワークショップ / 城一裕・金子智太郎(2010〜)
- Chaos Synthesizer
- 複雑系(Complexity)や創発(Emergence)という概念との深い関わり
- Generative Art の時間スケール
- The Rise of Long-Form Generative Art / Tyler Hobbs(2021)
- 実行され続ける Generative Art(自動長距離走)
- 人間が選択(キュレーション)しない Generative Art
- コード=アルゴリズムによる実行の中に美的判断を導入する
- アルゴリズムの自律性 → ベンゼの生成美学(逸脱のための美学)
- 「Generative Art に作家性や作品性はどこまで必要か?」
- Uncreative Coding
- Critical Coding としての Generative Coding
- Codingを他者に委ねる(AIの出力こそがGenerativeである)
- → AIを擬人化しない
- GitHub Copilot(2021)
- Uncreative Coding / アンクリエイティブ・コーディング(2022)
- Uncreative Quantum Coding / アンクリエイティブ・量子コーディング
- 指示した通りにできないからこそ面白い(Glitch Intelligence)
- Generative Coding:コードで風景を描く
- 意図:この風景を記憶と記録に残したい!
- ↓
- メディア:写真・映像・絵・デッサン・散文・詩・音 …
- ↓
- 方法:コードで風景を記録する(コードを出力するカメラ)
- 風景写真(800×800)
- 言語表現を介してコードを生成する
- GPT-4o
- p5.jsのコード生成
- 参考:GPT o1-preview(テキスト入力のみ)
- 言語表現を介さずにコードを生成する
- インストラクション・アート+ Generative Coding
- ソフトウェア構造 #003(Casey Leas)
- GitHub Copilot で生成されたコードと画像(2022年)
- Uncreative Codingで生成された画像を再びコードに戻す(GTP-4o)
- GTP-4o でコードを生成する(2024)
- Claude Sonnet 3.5
- 風景写真
- ソフトウェア構造 #003
- Generative Coding による構造デッサン
- Processingによる植物デッサン(久保田晃弘・田所淳, 20100510)
- Code as Creative Medium[コード・アズ・クリエイティブ・メディウム]
- GPT-4o
- メタ・プログラミングとGenerative Art
- コーディングの生成性(generativity)はどこにあるのか?
- 実行的価値:コード(アルゴリズム)のパフォーマティヴィティ
- コードが演じるパフォーマンスアート
- ・叙事演劇(ブレヒト):Generative Coding
- ・残酷演劇(アルトー):Meta Programming
- 自己削除プログラム(selfdelete.pde)
- 自己書き換えプログラム(rewrite.pde)
- リフレクション(プログラムが実行時に自身の構造を操作する)
- 生成規則を自己書き換えするコード(SelfModifyingFractal.pde)
- いまどきの生成AIは本当に「生成」AIなのか?
- ダグラス・ホフスタッターの「Strange Loop(不思議の環)」
- 「Strange Loop」の特徴
- プログラムが自身のコードを生成、操作、実行するメタプログラミングは、
- もつれた階層を持つ「Strange Loop」の性質と類似している。
- クワイン(ソースコードと同じ文字列を出力するプログラム)
- 出力
- 形式と内容は不可分であり、強い相互関係がある。
- 形式と内容を密接に関連させることが重要である。
- ホモ・ジェネラティオ(Homo Generatio)
- 「生成(Generation)」には長い歴史がある。
- 芸術の歴史において「生成」は正当に議論されてきたのか?
- Automat und Mensch(自動機械と人間)
- 自動機械と人間 2.0 — それから5年
- コーディングのスキル合戦はGenerativeではない?
- 短いコードを擁護する(In Defence of the Short Code)
- 10 PRINT CHR$(205.5+RND(1)); : GOTO 10
- Game of Life
- おまけ
- AIとUniversal Coding(シニアのためのコーディング環境)
- 配色に困った時には:
- Colormind - the AI powered color palette generator
- Adobe Color で美しいパレットを作成
- 風景を描くようになったきっかけ:
- 風景画のレッスン:形・構図・色の基礎知識 / ミッチェル・アルバラ
久保田晃弘(多摩美術大学)
Creative Coding と Generative Art を分離する
Code (and) Art
Generative Art:コードの実行結果としての視覚芸術
Algorithmic Music:コードの実行結果としての音楽
Code Poetry:プログラムコードで詩を書く
Creative Coding:プログラムを書いて創造的な作品を生み出す
創造的(Creative)であるとはどういうことか?
Creative Programmer / Wouter Groeneveld(2023)
クリエイティブプログラマー/ウーター・グローネフェルド
結論:プログラミングはあたりまえの行為である
Creativity 批判
Against Creativity / Oli Mould(2020)
Generative Coding
コードを書くことの意味を再考する
遺伝子組み換え(Creative Coding)と園芸(Generative Art)
Coding が面白いのか Generationが面白いのか?
Creative Coder なのか Generative Artist なのか?
人間ががんばってコードを書けば書くほどGenerative Artから遠ざかる?
人間がなるべく何もしない方がいいのかもしれない?
批判理論(Critical Theory)と解釈学(Hermeneutics)の必要性
Critical Code Studies(2020)
Live Coding: A User's Manual(2022)
ジェネラティブ・アート/マット・ピアソン(2012)
コンピュータを用いることは、Generative Artの必要条件ではない
Generative Art Timeline
生成音楽ワークショップ / 城一裕・金子智太郎(2010〜)
Chaos Synthesizer
複雑系(Complexity)や創発(Emergence)という概念との深い関わり
Generative Art の時間スケール
The Rise of Long-Form Generative Art / Tyler Hobbs(2021)
実行され続ける Generative Art(自動長距離走)
人間が選択(キュレーション)しない Generative Art
コード=アルゴリズムによる実行の中に美的判断を導入する
アルゴリズムの自律性 → ベンゼの生成美学(逸脱のための美学)
「Generative Art に作家性や作品性はどこまで必要か?」
Uncreative Coding
Critical Coding としての Generative Coding
Codingを他者に委ねる(AIの出力こそがGenerativeである)
→ AIを擬人化しない
GitHub Copilot(2021)
Uncreative Coding / アンクリエイティブ・コーディング(2022)
Uncreative Quantum Coding / アンクリエイティブ・量子コーディング
指示した通りにできないからこそ面白い(Glitch Intelligence)
Generative Coding:コードで風景を描く
意図:この風景を記憶と記録に残したい!
↓
メディア:写真・映像・絵・デッサン・散文・詩・音 …
↓
方法:コードで風景を記録する(コードを出力するカメラ)
風景写真(800×800)
言語表現を介してコードを生成する
GPT-4o
1.電柱 - 左側の中央近くに立っている。
2.電線 - 電柱から右方向に斜めに伸びている。複数本の電線が並行している。
3.山々 - 画面の背景に、複数の山のシルエットが見える。霧がかかっているため、山々の輪郭がぼんやりとしている。
4.霧 - 山々の中腹に霧がかかり、全体的に雲や霧が景色を覆っている。
5.空 - 背景の上部には曇り空が広がっており、薄明かりが差し込んでいる。
• 電柱:画面の左中央付近に配置され、縦方向に伸びている。
• 電線:電柱から右上にかけて、複数の電線が放射状に斜めに伸びている。電線の角度は画面の右上部に向かっている。
• 山々:画面下部に広がり、複数の山が重なり合っている。霧の影響で、遠くの山はぼんやりとしている。
• 空:画面上部に広がる空は、全体的に薄曇りの状態であり、太陽の光がほのかに見える。
function setup() {
createCanvas(800, 800);
background(200);
// 空の描写
noStroke();
fill(190, 210, 230);
rect(0, 0, 800, 400); // 空の部分
// 山の描写
fill(50, 70, 50); // 手前の山
beginShape();
vertex(0, 500);
vertex(200, 400);
vertex(400, 450);
vertex(600, 400);
vertex(800, 500);
vertex(800, 800);
vertex(0, 800);
endShape(CLOSE);
fill(80, 100, 80); // 奥の山
beginShape();
vertex(0, 550);
vertex(300, 450);
vertex(500, 500);
vertex(700, 450);
vertex(800, 550);
vertex(800, 800);
vertex(0, 800);
endShape(CLOSE);
// 霧の描写(透明度を持たせる)
fill(255, 255, 255, 100);
rect(0, 400, 800, 400);
// 電柱の描写
stroke(50);
strokeWeight(10);
line(200, 400, 200, 800); // 電柱
// 電線の描写
strokeWeight(2);
line(200, 420, 800, 350); // 1本目の電線
line(200, 440, 800, 370); // 2本目の電線
line(200, 460, 800, 390); // 3本目の電線
line(200, 480, 800, 410); // 4本目の電線
}
• 空の描写:背景の上半分を薄い青色で塗り、曇り空を表現。
• 山の描写:手前と奥に2層の山を描画し、山の重なりを表現。色の違いで遠近感を出しています。
• 霧の描写:山の上に薄い白色の長方形を描き、透明度を加えて霧を再現。
• 電柱と電線の描写:電柱を中央左に配置し、電線を斜めに引いています。
参考:GPT o1-preview(テキスト入力のみ)
function setup() {
createCanvas(800, 800);
}
function draw() {
drawSky();
drawMountains();
drawUtilityPole();
drawPowerLines();
}
function drawSky() {
background(200);
// Draw faint sunlight
noStroke();
fill(255, 255, 200, 50);
ellipse(width * 0.7, height * 0.2, 150, 150);
// Draw clouds using Perlin noise
noStroke();
for (let y = 0; y < height * 0.5; y += 5) {
for (let x = 0; x < width; x += 5) {
let noiseVal = noise(x * 0.01, y * 0.01);
fill(200 + noiseVal * 55, 200 + noiseVal * 55, 200 + noiseVal * 55, 100);
rect(x, y, 5, 5);
}
}
}
function drawMountains() {
noStroke();
let layers = 5;
for (let i = 0; i < layers; i++) {
let yOff = map(i, 0, layers, height * 0.6, height * 0.9);
let mountainColor = color(80 + i * 20, 80 + i * 20, 80 + i * 20);
fill(mountainColor);
beginShape();
vertex(0, height);
for (let x = 0; x <= width; x += 10) {
let y = yOff + map(noise(i * 1000, x * 0.005), 0, 1, -50, 50);
vertex(x, y);
}
vertex(width, height);
endShape(CLOSE);
// Draw fog over the mountain layer
if (i < layers - 1) {
let fogY = yOff - 50;
fill(255, 80 - i * 15);
rect(0, fogY, width, height - fogY);
}
}
}
function drawUtilityPole() {
let poleX = width * 0.1;
let poleTopY = height * 0.2;
let poleBottomY = height * 0.8;
// Draw the pole
stroke(50);
strokeWeight(8);
line(poleX, poleTopY, poleX, poleBottomY);
// Draw crossbars
strokeWeight(4);
let crossbarYPositions = [height * 0.35, height * 0.45, height * 0.55];
for (let y of crossbarYPositions) {
line(poleX - 20, y, poleX + 20, y);
}
}
function drawPowerLines() {
stroke(50);
strokeWeight(2);
let poleX = width * 0.1;
let crossbarYPositions = [height * 0.35, height * 0.45, height * 0.55];
for (let i = 0; i < crossbarYPositions.length; i++) {
let startX = poleX + 20;
let startY = crossbarYPositions[i];
let endX = width * 0.9;
let endY = height * 0.1 + i * 30;
line(startX, startY, endX, endY);
}
}
言語表現を介さずにコードを生成する
function setup() {
createCanvas(800, 800);
background(200, 220, 250); // Light sky
// Drawing mountains
drawMountains();
// Drawing power lines
drawPowerLines();
}
function drawMountains() {
noStroke();
// Dark green mountains
fill(50, 70, 50);
beginShape();
vertex(0, 500);
vertex(100, 450);
vertex(200, 480);
vertex(350, 400);
vertex(500, 500);
vertex(650, 450);
vertex(800, 500);
vertex(800, 800);
vertex(0, 800);
endShape(CLOSE);
// Lighter green distant mountains
fill(70, 90, 70, 150); // Adding transparency for fog effect
beginShape();
vertex(0, 550);
vertex(150, 500);
vertex(300, 550);
vertex(450, 500);
vertex(600, 550);
vertex(800, 600);
vertex(800, 800);
vertex(0, 800);
endShape(CLOSE);
}
function drawPowerLines() {
stroke(0);
strokeWeight(2);
// Power pole
line(150, 200, 150, 800); // main pole
strokeWeight(10);
point(150, 180); // top of the pole
strokeWeight(3);
// Power lines
line(0, 250, 800, 280);
line(0, 300, 800, 330);
line(0, 350, 800, 380);
line(0, 400, 800, 430);
// Small details on the pole (insulators)
strokeWeight(5);
point(140, 230);
point(160, 230);
strokeWeight(1);
}
function draw() {
// The fog effect can be layered on top
noStroke();
fill(200, 220, 240, 100); // semi-transparent white-blueish fog
rect(0, 450, 800, 350);
}
• 山:霧がかかったような2層の山を描きます。
• 送電線:いくつかの斜めの線が送電線を表し、基本的な電柱が細部まで描かれます。
• 霧: 透明の長方形で、画像の下半分を覆う霧の層をシミュレート。
let fogLayer;
function setup() {
createCanvas(800, 800);
background(200, 220, 250); // Light sky
// Drawing mountains
drawMountains();
// Drawing power lines
drawPowerLines();
// Creating a fog effect
fogLayer = createGraphics(800, 800);
drawFog();
// Applying the fog layer
image(fogLayer, 0, 0);
}
function drawMountains() {
noStroke();
// Dark green mountains
fill(50, 70, 50);
beginShape();
vertex(0, 500);
vertex(100, 450);
vertex(200, 480);
vertex(350, 400);
vertex(500, 500);
vertex(650, 450);
vertex(800, 500);
vertex(800, 800);
vertex(0, 800);
endShape(CLOSE);
// Lighter green distant mountains
fill(70, 90, 70, 150); // Adding transparency for fog effect
beginShape();
vertex(0, 550);
vertex(150, 500);
vertex(300, 550);
vertex(450, 500);
vertex(600, 550);
vertex(800, 600);
vertex(800, 800);
vertex(0, 800);
endShape(CLOSE);
}
function drawPowerLines() {
stroke(0);
strokeWeight(2);
// Power pole
line(150, 200, 150, 800); // main pole
strokeWeight(10);
point(150, 180); // top of the pole
strokeWeight(3);
// Power lines
line(0, 250, 800, 280);
line(0, 300, 800, 330);
line(0, 350, 800, 380);
line(0, 400, 800, 430);
// Small details on the pole (insulators)
strokeWeight(5);
point(140, 230);
point(160, 230);
strokeWeight(1);
}
function drawFog() {
fogLayer.noStroke();
fogLayer.fill(200, 220, 240, 50); // light fog color
// Using perlin noise for organic fog shapes
for (let i = 0; i < 800; i += 5) {
for (let j = 400; j < 800; j += 5) {
let noiseVal = noise(i * 0.01, j * 0.01);
let fogOpacity = map(noiseVal, 0, 1, 0, 100);
fogLayer.fill(200, 220, 240, fogOpacity);
fogLayer.ellipse(i, j, 10, 10);
}
}
}
1.Perlinノイズを使用:noise()関数を使って、霧にランダムな変化を加えました。これにより、霧の形がより自然に見えます。
2.透明度の変化:霧の透明度に変化を持たせ、ノイズに基づいて濃淡がランダムに見えるように調整しました。
3.fogLayerの使用:霧専用のレイヤーを作成し、後からキャンバス全体に適用しています。これにより、他のオブジェクトとの重なりが自然に表現されます。
let fogOffset = 0.0;
function setup() {
createCanvas(800, 600);
noLoop();
frameRate(30);
}
function draw() {
background(180, 200, 230); // Sky color
// Draw sky gradient for light effect
for (let y = 0; y < height * 0.6; y++) {
let inter = map(y, 0, height * 0.6, 0, 1);
let c = lerpColor(color(255, 220, 180), color(180, 200, 230), inter);
stroke(c);
line(0, y, width, y);
}
// Draw distant mountain
fill(80, 100, 80);
noStroke();
beginShape();
vertex(0, height * 0.5);
bezierVertex(width * 0.2, height * 0.4, width * 0.4, height * 0.6, width * 0.6, height * 0.5);
bezierVertex(width * 0.8, height * 0.4, width, height * 0.55, width, height);
vertex(0, height);
endShape(CLOSE);
// Draw middle mountain
fill(100, 120, 100);
beginShape();
vertex(0, height * 0.6);
bezierVertex(width * 0.1, height * 0.5, width * 0.3, height * 0.65, width * 0.5, height * 0.6);
bezierVertex(width * 0.7, height * 0.5, width, height * 0.65, width, height);
vertex(0, height);
endShape(CLOSE);
// Draw foreground mountain (darker for depth)
fill(60, 80, 60);
beginShape();
vertex(0, height * 0.7);
bezierVertex(width * 0.2, height * 0.6, width * 0.4, height * 0.75, width * 0.6, height * 0.7);
bezierVertex(width * 0.8, height * 0.65, width, height * 0.8, width, height);
vertex(0, height);
endShape(CLOSE);
// Fog effect
for (let i = 0; i < 4; i++) {
let fogHeight = height * 0.5 + i * 30;
drawFog(fogHeight, fogOffset + i * 0.1);
}
// Draw power lines
stroke(60);
strokeWeight(2);
let yOffsets = [height * 0.3, height * 0.35, height * 0.4, height * 0.45];
for (let y of yOffsets) {
line(0, y, width, y);
}
// Draw power pole
drawPowerPole(width * 0.2, height * 0.2, height);
fogOffset += 0.01;
requestAnimationFrame(draw);
}
function drawFog(y, offset) {
noStroke();
fill(240, 240, 240, 100);
beginShape();
vertex(0, y);
for (let x = 0; x < width; x += 20) {
vertex(x, y + noise(x * 0.02 + offset, y * 0.02) * 50);
}
vertex(width, y);
vertex(width, y + 100);
vertex(0, y + 100);
endShape(CLOSE);
}
function drawPowerPole(x, topY, bottomY) {
stroke(60);
strokeWeight(6);
line(x, topY, x, bottomY);
// Draw insulators
strokeWeight(4);
for (let i = 0; i < 3; i++) {
line(x - 10, topY + i * 20, x + 10, topY + i * 20);
}
}
function setup() {
createCanvas(800, 800); // キャンバスサイズを800x800に設定
noLoop(); // 一度だけ描画
}
function draw() {
background(210, 220, 230); // 空の柔らかい青色
// 山を描画 (ブロック状に)
fill(80, 100, 70);
beginShape();
vertex(0, 600);
vertex(100, 500);
vertex(300, 540);
vertex(500, 460);
vertex(700, 500);
vertex(800, 540);
vertex(800, 800);
vertex(0, 800);
endShape(CLOSE);
fill(100, 120, 90);
beginShape();
vertex(0, 540);
vertex(200, 460);
vertex(400, 480);
vertex(600, 440);
vertex(800, 480);
vertex(800, 800);
vertex(0, 800);
endShape(CLOSE);
// 電柱を描画
fill(50, 50, 50); // ダークグレーの電柱
rect(140, 240, 20, 360); // 電柱の位置とサイズ
// 電線を描画
stroke(0);
strokeWeight(4);
line(0, 320, 800, 280); // 一番上の電線
line(0, 360, 800, 320); // 二番目の電線
line(0, 400, 800, 360); // 三番目の電線
line(0, 440, 800, 400); // 四番目の電線
// 電柱上の絶縁体
fill(255);
ellipse(150, 300, 20, 20);
ellipse(150, 340, 20, 20);
ellipse(150, 380, 20, 20);
// 背景の霧を表現
noStroke();
fill(230, 240, 250, 150); // 半透明な霧
rect(0, 460, 800, 340);
}
• setup() 関数でキャンバスを作成し、背景色をGuy Yanaiのような柔らかい色調で設定しています。
• draw() 関数では、山をシンプルなブロック状に描き、Guy Yanaiのスタイルに寄せた配色と構図を採用しました。
• 電柱や電線も、シンプルな形で描いており、Guy Yanaiのスタイルに合わせたミニマルな表現を目指しています。
• 霧は半透明の長方形で背景に表現し、山の上にかかる霧の柔らかさを再現しています。
インストラクション・アート+ Generative Coding
ソフトウェア構造 #003(Casey Leas)
GitHub Copilot で生成されたコードと画像(2022年)
function setup() {
createCanvas(400, 400);
}
//A surface filled with one hundred medium to small sized circles. Each circle has a different size and direction, but moves at the same slow rate. Display:
//A.The instantaneous intersections of the circles
//B.The aggregate intersections of the circles
function draw() {
background(220);
stroke(0);
strokeWeight(2);
noFill();
for (var i = 0; i < 100; i++) {
var x = random(width);
var y = random(height);
var r = random(10, 20);
var d = random(1, 5);
var c = color(random(255), random(255), random(255));
fill(c);
ellipse(x, y, r, r);
ellipse(x + d, y + d, r, r);
ellipse(x - d, y - d, r, r);
ellipse(x + d, y - d, r, r);
ellipse(x - d, y + d, r, r);
}
Uncreative Codingで生成された画像を再びコードに戻す(GTP-4o)
function setup() {
createCanvas(800, 800);
background(200); // Gray background
let clusterCount = 50; // Number of clusters
for (let i = 0; i < clusterCount; i++) {
drawCluster(random(width), random(height));
}
}
function drawCluster(x, y) {
let circleCount = int(random(3, 7)); // Random number of circles per cluster
let clusterSize = random(20, 50); // Random size for the cluster
for (let i = 0; i < circleCount; i++) {
let angle = TWO_PI / circleCount * i;
let offsetX = cos(angle) * clusterSize;
let offsetY = sin(angle) * clusterSize;
let circleSize = random(15, 40); // Random size of each circle
// Random fill color for each circle
fill(random(255), random(255), random(255));
stroke(0); // Black outline
strokeWeight(3);
// Draw the circle
ellipse(x + offsetX, y + offsetY, circleSize, circleSize);
}
}
1.setup(): キャンバスを 800x800 ピクセルに設定し、背景をグレーで塗りつぶします。
2.drawCluster(x, y): この関数は、指定された (x, y) 位置の周囲に円のクラスターを生成します。円の数とサイズはランダムです。
3.ellipse(): 円は、画像内のクラスターと同様に、ランダムな色と黒いストロークで描画されます。
GTP-4o でコードを生成する(2024)
let circles = [];
let numCircles = 100;
let speed = 1; // Slow speed for all circles
let aggregateIntersections;
function setup() {
createCanvas(800, 800);
background(255);
noFill();
// Initialize the aggregate intersection buffer
aggregateIntersections = createGraphics(800, 800);
aggregateIntersections.background(255, 255, 255, 0); // Transparent background
// Create circles with random positions, sizes, and directions
for (let i = 0; i < numCircles; i++) {
let circle = {
x: random(width),
y: random(height),
size: random(20, 50), // Medium to small circles
dx: random(-speed, speed),
dy: random(-speed, speed)
};
circles.push(circle);
}
}
function draw() {
background(255);
// Draw aggregate intersections
image(aggregateIntersections, 0, 0);
// Draw and move circles
for (let i = 0; i < numCircles; i++) {
let c = circles[i];
moveCircle(c);
ellipse(c.x, c.y, c.size);
// Check for intersections with other circles
for (let j = i + 1; j < numCircles; j++) {
let other = circles[j];
if (isIntersecting(c, other)) {
displayInstantaneousIntersection(c, other);
recordAggregateIntersection(c, other);
}
}
}
}
// Function to move the circle and bounce off the edges
function moveCircle(circle) {
circle.x += circle.dx;
circle.y += circle.dy;
// Bounce off walls
if (circle.x < 0 || circle.x > width) circle.dx *= -1;
if (circle.y < 0 || circle.y > height) circle.dy *= -1;
}
// Check if two circles intersect
function isIntersecting(c1, c2) {
let d = dist(c1.x, c1.y, c2.x, c2.y);
return d < (c1.size / 2 + c2.size / 2);
}
// Display the instantaneous intersections in red
function displayInstantaneousIntersection(c1, c2) {
stroke(255, 0, 0);
strokeWeight(2);
let ix = (c1.x + c2.x) / 2;
let iy = (c1.y + c2.y) / 2;
let intersectionSize = (c1.size + c2.size) / 4;
ellipse(ix, iy, intersectionSize);
}
// Record the intersection permanently on the aggregate layer
function recordAggregateIntersection(c1, c2) {
aggregateIntersections.stroke(0, 0, 255, 50); // Blue with transparency for aggregate intersections
aggregateIntersections.strokeWeight(2);
let ix = (c1.x + c2.x) / 2;
let iy = (c1.y + c2.y) / 2;
let intersectionSize = (c1.size + c2.size) / 4;
aggregateIntersections.ellipse(ix, iy, intersectionSize);
}
1.円の初期化: 位置、サイズ、方向がランダムな 100 個の円 (numCircles) を作成します。各円は低速で移動します (速度 = 1)。
2.移動: 円は一定の低速で移動し、キャンバスの端に当たると跳ね返ります。
3.交差点の検出: 各フレームで、isIntersecting() 関数を使用して 2 つの円が交差しているかどうかを確認します。この関数は、円の中心間の距離と円の半径の合計を比較します。
4.瞬間交差点 (パート A): 円が交差すると、交差点に瞬間交差点を表す赤い楕円が描画されます。これはメインの draw() 関数で実行されるため、リアルタイムで更新されます。
5.集約交差点 (パート B): 過去の交差点をすべて保存するために、別のグラフィック レイヤー (aggregateIntersections) が使用されます。交差が発生すると、このレイヤーに青い半透明の楕円が描画され、交差の累積履歴が表されます。このレイヤーは、過去の交差を保存しながら、すべてのフレームで背景の上に表示されます。
Claude Sonnet 3.5
風景写真
function setup() {
createCanvas(800, 800);
}
function draw() {
background(200, 210, 220);
// Sky
noStroke();
fill(240, 240, 250);
rect(0, 0, width, height * 0.6);
// Clouds
fill(220, 220, 230, 150);
ellipse(200, 100, 200, 80);
ellipse(600, 150, 300, 100);
// Mountains
fill(50, 70, 50);
beginShape();
vertex(0, height);
vertex(200, 400);
vertex(400, 600);
vertex(600, 450);
vertex(800, 550);
vertex(800, height);
endShape(CLOSE);
// Fog
fill(200, 210, 220, 150);
beginShape();
vertex(0, 500);
vertex(200, 550);
vertex(400, 500);
vertex(600, 600);
vertex(800, 550);
vertex(800, height);
vertex(0, height);
endShape(CLOSE);
// Power pole
stroke(30);
strokeWeight(10);
line(400, 200, 400, height);
// Cross beam
line(350, 250, 450, 250);
// Power lines
strokeWeight(2);
line(0, 230, 800, 270);
line(0, 250, 800, 290);
line(0, 270, 800, 310);
// Insulators
strokeWeight(5);
point(370, 250);
point(400, 250);
point(430, 250);
}
function setup() {
createCanvas(800, 800);
}
function draw() {
// Sky
background(200, 210, 220);
// Brighter area in the sky (sunset/sunrise)
noStroke();
fill(255, 240, 200, 100);
ellipse(650, 100, 300, 200);
// Clouds
fill(255, 255, 255, 200);
ellipse(600, 50, 200, 80);
ellipse(100, 100, 150, 60);
ellipse(300, 80, 100, 40);
// Mountains (3 layers)
for (let i = 0; i < 3; i++) {
fill(30 - i * 10, 50 - i * 10, 30 - i * 10);
beginShape();
for (let x = 0; x <= width; x += 20) {
let y = noise(i * 0.5 + x * 0.002) * 200 + 400 + i * 100;
vertex(x, y);
}
vertex(width, height);
vertex(0, height);
endShape(CLOSE);
}
// Fog
fill(200, 210, 220, 150);
for (let y = 500; y < height; y += 50) {
beginShape();
for (let x = 0; x <= width; x += 20) {
let fogY = y + noise(x * 0.01, y * 0.01) * 100;
vertex(x, fogY);
}
vertex(width, height);
vertex(0, height);
endShape(CLOSE);
}
// Power pole
stroke(100, 70, 40);
strokeWeight(10);
line(300, 200, 300, height);
// Cross beam
strokeWeight(8);
line(250, 250, 350, 250);
// Insulators
fill(0);
noStroke();
ellipse(270, 250, 10, 10);
ellipse(300, 250, 10, 10);
ellipse(330, 250, 10, 10);
// Power lines
stroke(0);
strokeWeight(2);
for (let i = 0; i < 6; i++) {
bezier(0, 220 + i * 20, 300, 250, 500, 270, width, 240 + i * 20);
}
// Device on the power line
fill(100);
noStroke();
ellipse(700, 320, 15, 15);
}
let circles = [];
function setup() {
createCanvas(800, 800);
background(240);
// Generate circles
for (let i = 0; i < 200; i++) {
circles.push({
x: random(width),
y: random(height),
size: random(10, 30),
color: color(random(255), random(255), random(255))
});
}
}
function draw() {
background(240);
// Draw circles
for (let circle of circles) {
drawCircleGroup(circle.x, circle.y, circle.size, circle.color);
}
noLoop(); // Draw once
}
function drawCircleGroup(x, y, size, baseColor) {
let count = floor(random(1, 5)); // 1 to 4 circles in a group
for (let i = 0; i < count; i++) {
let offsetX = random(-5, 5);
let offsetY = random(-5, 5);
fill(baseColor);
stroke(0);
strokeWeight(2);
ellipse(x + offsetX, y + offsetY, size);
}
}
ソフトウェア構造 #003
let circles = [];
let intersectionCanvas;
function setup() {
createCanvas(800, 800);
intersectionCanvas = createGraphics(800, 800);
// Generate 100 circles
for (let i = 0; i < 100; i++) {
circles.push({
x: random(width),
y: random(height),
size: random(10, 30),
angle: random(TWO_PI),
speed: 0.5
});
}
}
function draw() {
background(240);
// Draw aggregate intersections
image(intersectionCanvas, 0, 0);
// Update and draw circles, check for instantaneous intersections
for (let i = 0; i < circles.length; i++) {
updateCircle(circles[i]);
// Check intersections with other circles
for (let j = i + 1; j < circles.length; j++) {
if (circlesIntersect(circles[i], circles[j])) {
// Draw instantaneous intersection
fill(255, 0, 0, 100);
noStroke();
ellipse((circles[i].x + circles[j].x) / 2, (circles[i].y + circles[j].y) / 2, 5);
// Draw on aggregate intersection canvas
intersectionCanvas.fill(255, 0, 0, 10);
intersectionCanvas.noStroke();
intersectionCanvas.ellipse((circles[i].x + circles[j].x) / 2, (circles[i].y + circles[j].y) / 2, 5);
}
}
}
// Draw circles
noFill();
stroke(0);
for (let circle of circles) {
ellipse(circle.x, circle.y, circle.size);
}
}
function updateCircle(circle) {
circle.x += cos(circle.angle) * circle.speed;
circle.y += sin(circle.angle) * circle.speed;
// Wrap around edges
circle.x = (circle.x + width) % width;
circle.y = (circle.y + height) % height;
}
function circlesIntersect(c1, c2) {
let distance = dist(c1.x, c1.y, c2.x, c2.y);
return distance < (c1.size / 2 + c2.size / 2);
}
Generative Coding による構造デッサン
Processingによる植物デッサン(久保田晃弘・田所淳, 20100510)
Code as Creative Medium[コード・アズ・クリエイティブ・メディウム]
GPT-4o
function setup() {
createCanvas(800, 800);
background(240);
// 緑の背景
noStroke();
fill(100, 170, 100);
rect(0, 0, 800, 800);
// 葉を描く関数
drawLeaf(400, 400, 150, color(50, 120, 50));
drawLeaf(200, 300, 100, color(70, 160, 70));
drawLeaf(600, 500, 120, color(80, 140, 80));
// 分岐した茎の描画
stroke(100, 200, 100);
strokeWeight(5);
drawBranch(400, 400, 100, 0);
}
function drawLeaf(x, y, size, leafColor) {
fill(leafColor);
noStroke();
beginShape();
for (let i = 0; i < TWO_PI; i += PI / 6) {
let px = x + cos(i) * size;
let py = y + sin(i) * size * 0.6;
vertex(px, py);
}
endShape(CLOSE);
}
function drawBranch(x, y, length, angle) {
if (length < 20) return;
let endX = x + cos(angle) * length;
let endY = y + sin(angle) * length;
line(x, y, endX, endY);
// 分岐する
drawBranch(endX, endY, length * 0.7, angle + PI / 4);
drawBranch(endX, endY, length * 0.7, angle - PI / 4);
}
1.背景: 背景は緑色で塗りつぶしています。
2.葉の描画: drawLeaf 関数は、シンプルな楕円形の葉を描画します。葉のサイズや位置をパラメータとして調整できます。
3.茎の分岐描画: drawBranch 関数を使用して、再帰的に分岐する茎を描いています。これは、実際の植物の複雑な構造を模倣するための基本的なアルゴリズムです。
function setup() {
createCanvas(800, 800);
background(200, 230, 200); // light green background
// Drawing the leaves (simplified)
drawLeaves();
// Drawing the vine (spiral)
drawVine();
}
function drawLeaves() {
fill(50, 140, 50); // dark green color for the leaves
noStroke();
// Left leaf
ellipse(300, 500, 200, 100);
ellipse(350, 450, 200, 100);
// Right leaf
ellipse(500, 500, 200, 100);
ellipse(450, 450, 200, 100);
// Top-left leaf
ellipse(400, 300, 150, 80);
}
function drawVine() {
stroke(200, 255, 100); // vine color (light yellow-green)
strokeWeight(6);
noFill();
// Draw the spiral
beginShape();
for (let t = 0; t < TWO_PI * 5; t += 0.1) {
let r = 20 + t * 5; // radius increases with time
let x = 400 + cos(t) * r;
let y = 400 + sin(t) * r;
vertex(x, y);
}
endShape();
}
function draw() {
// Nothing in draw for now
}
- 楕円を使って3つの単純化された葉の形を描きます。
- 三角関数を使用して描画されたループ状の曲線で、らせん状のつるを表現しています。
- 背景は写真と同じような薄い緑色。
メタ・プログラミングとGenerative Art
コーディングの生成性(generativity)はどこにあるのか?
実行的価値:コード(アルゴリズム)のパフォーマティヴィティ
コードが演じるパフォーマンスアート
・叙事演劇(ブレヒト):Generative Coding
・残酷演劇(アルトー):Meta Programming
自己削除プログラム(selfdelete.pde)
import java.io.File;
void setup() {
size(400, 400);
background(#474744);
noStroke();
// 円を描画
fill(#AB977B);
ellipse(random(width), random(height), 100, 100);
// 一定時間待ってからファイルを削除
delay(2000);
deleteSketchFile();
}
void deleteSketchFile() {
try {
File sketchFile = new File(sketchPath("selfdelete.pde"));
if (sketchFile.exists()) {
if (sketchFile.delete()) {
println("selfdelete.pde が正常に削除されました");
} else {
println("ファイルの削除に失敗しました");
}
} else {
println("ファイルが見つかりません");
}
} catch (Exception e) {
println("ファイルの削除中にエラーが発生しました: " + e.getMessage());
}
}
selfdelete.pde が正常に削除されました
自己書き換えプログラム(rewrite.pde)
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
void setup() {
size(400, 400);
background(#F8E755);
noStroke();
// 円を描画
fill(#B33934);
ellipse(random(width), random(height), 100, 100);
// ソースコードを書き換える
modifySketchFile();
}
void modifySketchFile() {
try {
File sketchFile = new File(sketchPath("rewrite.pde"));
if (sketchFile.exists()) {
PrintWriter writer = new PrintWriter(new FileWriter(sketchFile));
writer.println("void setup() {");
writer.println(" size(400, 400);");
writer.println(" background(#2B4347);"); // 背景色を変更
writer.println("}");
writer.println("void draw() {");
writer.println(" stroke(#F7F18D);");
writer.println(" line(random(width), random(height), random(width), random(height));"); // ランダムに線を引く
writer.println("}");
writer.flush();
writer.close();
println("rewrite.pde が正常に書き換えられました");
} else {
println("rewrite.pde ファイルが見つかりません");
}
} catch (Exception e) {
println("ファイルの書き換え中にエラーが発生しました: " + e.getMessage());
}
}
rewrite.pde が正常に書き換えられました
void setup() {
size(400, 400);
background(#2B4347);
}
void draw() {
stroke(#F7F18D);
line(random(width), random(height), random(width), random(height));
}
リフレクション(プログラムが実行時に自身の構造を操作する)
import java.lang.reflect.Method;
void setup() {
size(400, 400);
background(#C43F16);
noStroke();
try {
Class<?> c = this.getClass();
Method m = c.getDeclaredMethod("drawCircle");
m.invoke(this);
} catch (Exception e) {
e.printStackTrace();
}
}
void drawCircle() {
fill(#F3E9B8);
rect(random(width), random(height), 100, 100);
}
生成規則を自己書き換えするコード(SelfModifyingFractal.pde)
String rule = "F+F-F-F+F"; // 初期生成規則
int iteration = 0; // 現在の反復回数
void setup() {
size(800, 800);
background(#089AB2);
stroke(0);
noLoop(); // draw()を1回だけ実行
// 設定ファイルからルールと反復回数を読み込む
loadSettings();
// フラクタルを描画
pushMatrix();
translate(width/2, height/2);
drawFractal(rule, iteration);
popMatrix();
// 生成規則を自己書き換え
modifyRule();
// ルールと反復回数を設定ファイルに保存
saveSettings();
}
void draw() {
// 描画はsetup()内で行うため、ここでは何もしません
}
void drawFractal(String axiom, int iter) {
String current = axiom;
// 指定された回数だけルールを適用
for (int i = 0; i < iter; i++) {
current = applyRule(current);
}
// トータルの角度
float angle = radians(random(80, 100));
float length = random(4, 6);
// 描画
for (int i = 0; i < current.length(); i++) {
char c = current.charAt(i);
if (c == 'F') {
stroke(#FCA715);
strokeWeight(2);
line(0, 0, length, 0);
translate(length, 0);
} else if (c == '+') {
rotate(angle);
} else if (c == '-') {
rotate(-angle);
}
}
}
String applyRule(String input) {
StringBuilder output = new StringBuilder();
for (int i = 0; i < input.length(); i++) {
char c = input.charAt(i);
if (c == 'F') {
// 'F'をルールで置き換え
output.append(rule);
} else {
output.append(c);
}
}
return output.toString();
}
void modifyRule() {
// ルールを自己書き換え(例としてランダムに'+'か'-'を追加)
char[] operators = {'+', '-'};
char randomOp = operators[int(random(operators.length))];
rule += randomOp + "F";
// 反復回数を増やす
iteration++;
}
void saveSettings() {
String[] settings = {
"rule=" + rule,
"iteration=" + iteration
};
saveStrings("settings.txt", settings);
}
void loadSettings() {
String[] settings = null;
try {
settings = loadStrings("settings.txt");
} catch (Exception e) {
// 設定ファイルがない場合は初期値を使用
return;
}
// settings が null でないことを確認
if (settings == null) {
// 設定ファイルが読み込めなかった場合は初期値を使用
return;
}
for (String line : settings) {
if (line.startsWith("rule=")) {
rule = line.substring(5);
} else if (line.startsWith("iteration=")) {
iteration = int(line.substring(10));
}
}
}
rule=F+F-F-F+F
iteration=0
rule=F+F-F-F+F-F+F-F-F+F-F-F
iteration=7
いまどきの生成AIは本当に「生成」AIなのか?
ダグラス・ホフスタッターの「Strange Loop(不思議の環)」
「Strange Loop」の特徴
プログラムが自身のコードを生成、操作、実行するメタプログラミングは、
もつれた階層を持つ「Strange Loop」の性質と類似している。
クワイン(ソースコードと同じ文字列を出力するプログラム)
void setup() {
String code = "void setup() {%1$c String code = %3$c%2$s%3$c;%1$c println(String.format(code, 10, code, 34));%1$c exit();%1$c}";
println(String.format(code, 10, code, 34));
exit();
}
出力
void setup() {
String code = "void setup() {%1$c String code = %3$c%2$s%3$c;%1$c println(String.format(code, 10, code, 34));%1$c exit();%1$c}";
println(String.format(code, 10, code, 34));
exit();
}
形式と内容は不可分であり、強い相互関係がある。
形式と内容を密接に関連させることが重要である。
ホモ・ジェネラティオ(Homo Generatio)
「生成(Generation)」には長い歴史がある。
芸術の歴史において「生成」は正当に議論されてきたのか?
Automat und Mensch(自動機械と人間)
自動機械と人間 2.0 — それから5年
コーディングのスキル合戦はGenerativeではない?
短いコードを擁護する(In Defence of the Short Code)
10 PRINT CHR$(205.5+RND(1)); : GOTO 10
10 PRINT CHR$(205.5+RND(1));:GOTO 10
Game of Life
おまけ
AIとUniversal Coding(シニアのためのコーディング環境)
配色に困った時には:
Colormind - the AI powered color palette generator
Adobe Color で美しいパレットを作成
風景を描くようになったきっかけ:
風景画のレッスン:形・構図・色の基礎知識 / ミッチェル・アルバラ