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)


「Against Creativity」において、オリ・モールドは、現代社会、特に資本主義社会における創造性の概念を批判的に分析している。モールドは、創造性が本来持つ解放的な可能性を認めつつも、現代社会においては、資本主義の論理に取り込まれ、その成長と不平等を永続させるために利用されていると主張する。

創造性の経済化:現代社会では創造性が経済成長の主要なパラダイムとして位置づけられており、あらゆる場面で「創造的であれ」という圧力が個人に課せられている。そのため労働者は、より不安定な雇用形態や労働条件を受け入れざるを得なくなり、労働と余暇の境界線は曖昧になり、生活のあらゆる側面が資本主義の論理に侵食されている。

創造性の搾取:資本主義は創造性をその成長のために利用し、その結果生み出されたものを中央集権化し、収益化している。創造的な仕事は、資本主義の拡大を促進し、より公正で平等な社会の構築には貢献していない。

創造性の個人化:本書は、現代の創造性の言説は、共同作業や協調性を重視しながらも、実際には個人主義的で自己利益を追求する働き方を助長している。この個人化が、資本主義の不平等をさらに悪化させている。

創造性の画一化:「誰もが創造的である」という主張は、実際には、すべての労働を創造的なものとして再定義することで、資本主義の搾取的な労働構造を正当化するために利用されている。

代替的な創造性の排除:資本主義の論理に適合しない、よりラディカルで解放的な創造性にこそ可能性がある。共同労働、協同組合、社会運動など、資本主義の不平等や搾取に抵抗する実践こそが、より公正で平等な社会を創造する可能性を秘めている。

真の創造性を取り戻すためには、資本主義の論理から脱却し、より公正で平等な社会を構築するために、創造性を解放する必要がある。




Generative Coding

コードを書くことの意味を再考する





遺伝子組み換え(Creative Coding)と園芸(Generative Art)



Coding が面白いのか Generationが面白いのか?

  • Codingは人間による精緻な手作業(だった)
  • Generationは機械による実行(に限らない)

Creative Coder なのか Generative Artist なのか?



人間ががんばってコードを書けば書くほどGenerative Artから遠ざかる?

人間がなるべく何もしない方がいいのかもしれない?




批判理論(Critical Theory)と解釈学(Hermeneutics)の必要性


批判理論:社会の不公正や権力構造を批判的に分析し変革を目指す
解釈学:テクストや行為、現象を解釈し、その背後にある意味や意図を探る




Critical Code Studies(2020)


マーク・C・マリーノの「クリティカル・コード・スタディーズ」はコードを文化的なレンズを通して分析する新しい学問分野を提示する。マリーノは、コードが単なる機能的な命令ではなく、社会的、文化的、政治的な意味を持つ言語であると主張する。その意味を解き明かすには、特定の技術的な知識だけでなく、人文科学的な分析方法も必要である。

マリーノは、コードを解釈するための方法論、特にコードの「余剰機能」を理解することの重要性を強調している。マリーノの議論は、具体的な事例、たとえば「気候変動に関する研究者のメール流出事件」や「国境を越える移民のためのツール」のコードなどを通して展開されている。これらの事例を通して、彼は、コードが人間の行動や社会構造をどのように形成するか、そしてコード解釈が政治的な問題にどのように結びつくのかを明らかにしようとする。

マリーノが重視するのは、コードの社会的な側面、コードの解釈の多様性、そしてコードの解釈における人文科学的なアプローチである。マリーノは、コードを理解することは、単にプログラミングの知識を学ぶことだけでなく、人間の文化、社会、政治をより深く理解することにつながると主張する。


Live Coding: A User's Manual(2022)


パフォーマンス的、即興的、その場で行う:ライブコーディングとは、コードを介して人々が世界や互いにどのように関わり合うかということである。ここ数十年の間に、ライブコーディングはダイナミックな創造的実践として登場し、音楽や視覚芸術からコンピューターサイエンスまで、文化や技術の分野を超えて注目を集めるようになった。『ライブコーディング:ユーザーガイド』は、ライブコーディングの実践に関する初めての包括的な入門書であり、ライブコーディングが現代の文化生産や計算機文化についてより深い問いを開拓する可能性について、より幅広い文化的な論評を行っている。アーティストやミュージシャン、ソフトウェアデザイナー、研究者らによる共著である本書は、ライブコーディングの起源、理想、進化について、ライブコーディングの実践者による幅広い解説を含め、実践に焦点を当てた説明を提供している。より概念的な観点では、著者はライブコーディングと関連して、ライブ性、時間性、知識について考察し、その実践の将来の形について思索する。




ジェネラティブ・アート/マット・ピアソン(2012)


監訳者のことば(冒頭部分)

美術、芸術にとって「生成(generative/generation)」という概念は、とても重要な意味を持っています。ベンヤミンが1936年に発表した有名な論文『複製技術の時代における芸術作品』は、写真や映画という複製メディアの出現によって、芸術作品の価値、見方がどう変わったかについて、複製技術と知覚の面から論じていました。その後表現のメディアは機械から情報へと移行し、複製不可能な作品が持つ一回性、唯一性から生まれる「アウラ」が、デジタル技術によって可能になった非破壊ノンリニア編集による多数のヴァージョンの並置によって、分散型、並列型の「アウラ」へと変容していきました。

さらにメディアは、結果を記録するデータから、プロセスを記述できるアルゴリズム、プログラムへと移行していきます。これからの音楽家はCDではなくアプリを配布し、これからの映像作家はDVDやBlu-rayではなく、ソフトウェアを実行します。この「生成」するメディアによって形成される知覚様式とその価値は、礼拝的な古代の価値でも、展示的な現代の価値でもなく、日々変化するウェブ・スペースがそうであるように、それが「今まさに目の前で計算され、生成している」という、生成的価値を持ちます。その価値は、従来の複製技術時代を超えて「生成技術の時代の芸術作品とは何か」という新たな問いを私たちに投げかけることになりました。




コンピュータを用いることは、Generative Artの必要条件ではない


Generative Artの本質は、アーティストが直接作品を制作するのではなく、作品を生成するシステムやルールを設計することにある。このシステムやルールには、一般的に以下のような特徴がある。

  • アルゴリズムや数学的/機械的/無作為的な過程を用いる
  • ある程度の自律性を持つ
  • 予測不可能性や複雑性を含む

コンピュータは、これらの特徴を実現するための強力なツールですが、それを用いることは必須ではない。

したがって、Generative Artはコンピュータを用いなくても実現可能であり、その歴史はコンピュータの登場以前にさかのぼることができる。現在のGenerative Artではコンピュータが広く使用されていることは間違いはないが、Generative Artは、コンピュータのアルゴリズムによって生成されるアート作品に限られない。


Explore the 70,000-year history of generative art through
hundreds of milestones and ten chapters, edited by Peter Bauman



生成音楽ワークショップ / 城一裕・金子智太郎(2010〜)


Chaos Synthesizer





複雑系(Complexity)や創発(Emergence)という概念との深い関わり









Generative Art の時間スケール



The Rise of Long-Form Generative Art / Tyler Hobbs(2021)


新たな芸術形態が台頭しつつある。ジェネラティブアートは1960年代から存在していたが、新しいオンチェーンのジェネラティブアートプラットフォームは、このメディアを刺激的な新たな方向へと押し進めている。ジェネラティブ技術の多くは同じであるが、プログラム出力の目標は以前とは大きく異なっている。スクリプトからビューアーへの直接的な経路、および多数の反復により、アーティストは特別な芸術的アルゴリズムのクラスを作成するよう促される。ここではそれを「長編の生成主義(Long-Form Generativism)」と呼ぶ。本稿では、これらのアルゴリズムが異なる点と、その品質を分析する方法について論じる。


実行され続ける 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)


言語表現を介してコードを生成する


この写真に映っているものを全て書き出して、映っているものとその位置、画面上の構成について説明してください。次にその説明に従って、p5.jsのコードを生成してください。キャンバスサイズは、800×800とします。

GPT-4o


写真に含まれる要素
1.電柱 - 左側の中央近くに立っている。
2.電線 - 電柱から右方向に斜めに伸びている。複数本の電線が並行している。
3.山々 - 画面の背景に、複数の山のシルエットが見える。霧がかかっているため、山々の輪郭がぼんやりとしている。
4.霧 - 山々の中腹に霧がかかり、全体的に雲や霧が景色を覆っている。
5.空 - 背景の上部には曇り空が広がっており、薄明かりが差し込んでいる。

画面上の構成
• 電柱:画面の左中央付近に配置され、縦方向に伸びている。
• 電線:電柱から右上にかけて、複数の電線が放射状に斜めに伸びている。電線の角度は画面の右上部に向かっている。
• 山々:画面下部に広がり、複数の山が重なり合っている。霧の影響で、遠くの山はぼんやりとしている。
• 空:画面上部に広がる空は、全体的に薄曇りの状態であり、太陽の光がほのかに見える。

p5.jsのコード生成

以下は、これらの要素をp5.jsで描写するためのサンプルコードです。

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);
  }
}



言語表現を介さずにコードを生成する



この写真を模写するp5.jsコードを生成してください。キャンバスサイズは800×800とします。

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);
  }
}





Guy Yanaiのようなテイストで、p5.jsのコードで描いてくれますか?


Guy Yanaiの作品は、シンプルな構図と鮮やかなブロック状の色使いで、特徴的な現代的なスタイルを持っています。これを元に、p5.jsで写真のような風景を描くコードを作成してみます。電柱、電線、山のシルエット、そして背景の霧がかった空をシンプルな形状と色で表現してみます。

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のスタイルに合わせたミニマルな表現を目指しています。
• 霧は半透明の長方形で背景に表現し、山の上にかかる霧の柔らかさを再現しています。

このコードはGuy Yanaiのテイストを参考に、シンプルで色分けされた風景画をp5.jsで再現するものです。実行してみることで、スタイルに応じた調整も可能です。






インストラクション・アート+ Generative Coding


ソフトウェア構造 #003(Casey Leas)



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

100個の中〜小サイズの円で埋め尽くされた画面。それぞれの円は大きさも向きも違うが、ゆっくりと同じ速度で動く。

A. 各円の瞬時の交点
B. 各円の交点の集合体
を表示しなさい。


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)


この図形を生成するp5.jsコードを書いてください。キャンバスサイズは800×800とします。

この画像は、黒で輪郭を描かれたカラフルな円が複数集まっているように見えます。p5.js を使用して同様のパターンを作成するには、この効果のランダム バージョンを生成するコード スニペットを次に示します。

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(): 円は、画像内のクラスターと同様に、ランダムな色と黒いストロークで描画されます。

このコードを p5.js Web エディターまたはローカル開発環境にコピーして、出力を確認できます。clusterCount、circleCount、clusterSize などのパラメーターを調整して、さまざまな効果を実現できます。




GTP-4o でコードを生成する(2024)


次の指示にしたがって、p5.jsのコードを書いてください。キャンバスサイズは800×800とします。

p5.js で目的の動作を実現するには、まず、円がゆっくりと連続的にさまざまな方向に移動しながら、交差点も検出するシステムを実装する必要があります。瞬間交差点と集合交差点の両方を表示するには、2 つのレイヤーを使用できます。1 つはフレームごとに更新される瞬間交差点用、もう 1 つは過去のすべての交差点を追跡する集合交差点用です。

これを p5.js で実装する方法は次のとおりです。

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) が使用されます。交差が発生すると、このレイヤーに青い半透明の楕円が描画され、交差の累積履歴が表されます。このレイヤーは、過去の交差を保存しながら、すべてのフレームで背景の上に表示されます。

p5.js でこのコードを試して、動作を視覚化できます。




Claude Sonnet 3.5



風景写真


この画像を模写するp5.jsコードを生成します。キャンバスサイズを800x800として、画像の主要な要素を再現します。

このコードは、画像の主要な要素を再現しています:

  • 空と雲
  • 山々
  • 電柱と横木
  • 電線

p5.jsを使用して、これらの要素を描画しています。背景、山、霧にはbeginShape()とendShape()を使用して不規則な形状を作成しています。電柱、横木、電線は直線で表現しています。

このスケッチを実行すると、元の画像に似た風景が表示されます。色や形状は完全に一致しませんが、全体的な雰囲気を捉えています。

コードを修正して、色や形状をさらに調整したり、アニメーションを追加したりすることができます。例えば、霧を動かしたり、空の色を時間とともに変化させたりすることが可能です。


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);
}


この写真に映っているものを全て書き出して、映っているものとその位置、画面上の構成について説明してください。次にその説明に従って、p5.jsのコードを生成してください。キャンバスサイズは、800×800とします。


この写真に映っているものとその構成について説明します:

  • 空:画面上部を占め、薄い灰色がかった青色。右上に明るい領域があり、日の出や日没を示唆。
  • 雲:空全体に散在。右上に大きな白い雲、左上にも小さな雲がある。
  • 山々:画面下半分を占める。手前から奥に向かって3層の山が見える。濃い緑色で、霧に覆われている部分もある。
  • 霧:山の間を漂う。特に中央の山の周りに顕著。
  • 電柱:画面中央左よりに1本。褐色で、上部に横木がある。
  • 電線:画面全体を横切る。少なくとも6本の線が見える。電柱から放射状に広がっている。
  • がいし:電柱の横木に取り付けられている。3つの黒い点として見える。
  • 電線上の装置:右側の電線上に1つ、小さな円形の物体が見える。

この構成を基に、p5.jsのコードを生成します。
Detailed p5.js sketch of mountain landscape with power linesClick to open code
このコードは、あなたの説明に基づいて画像の要素をより詳細に再現しています:

  • 空の色合いと右上の明るい領域を表現
  • 複数の雲を追加
  • 3層の山々をnoise()関数を使用してより自然な形状で描画
  • 霧を複数層で表現し、より自然な効果を追加
  • 電柱と横木をより正確な位置に配置
  • がいしを3つの黒い点として表現
  • 6本の電線をbezier()関数を使用して曲線で描画
  • 右側の電線上に小さな装置を追加

このスケッチを実行すると、元の画像により近い風景が表示されます。色合い、形状、配置などをさらに調整することで、より正確な再現が可能です。また、霧の動きや空の色の変化などのアニメーション効果を追加することもできます。

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);
}




この画像を再現するp5.jsコードを作成します。カラフルな円を多数描画し、ランダムに配置します。

このコードは以下のように動作します:
  • setup() 関数で200個の円のデータ(位置、サイズ、色)を生成します。
  • draw() 関数で背景を描画し、各円のグループを描画します。
  • drawCircleGroup() 関数は、1〜4個の円を少しずつずらして描画することで、画像のような重なり合った円の効果を生み出します。
  • 円の色はランダムに生成され、黒い輪郭線が付けられます。
  • noLoop() を使用して1回だけ描画し、静的な画像を生成します。

このスケッチを実行すると、元の画像に似たカラフルな円のパターンが生成されます。ランダム性により、毎回少しずつ異なるパターンが生成されます。

円の数、サイズ範囲、色の選択方法などのパラメータを調整することで、より元の画像に近い結果を得ることができます。また、draw() 関数内の noLoop() を削除し、円の位置を少しずつ動かすことで、アニメーション効果を追加することも可能です。

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




ご要望に基づいて、動く円とその交差を表示するp5.jsコードを作成します。
p5.js sketch of moving circles with intersectionsClick to open code
このコードは以下のように動作します:

  • setup() 関数で100個の円を生成し、それぞれにランダムな位置、サイズ、方向を割り当てます。
  • draw() 関数で以下の処理を行います:

  • 集積的な交差を表示するキャンバスを描画
  • 各円を更新し、他の円との交差をチェック
  • 瞬間的な交差を赤い半透明の点で表示
  • 集積的な交差を別のグラフィックスオブジェクトに描画(徐々に蓄積されていく)
  • すべての円を描画

  • updateCircle() 関数で円を移動させ、画面端でラップアラウンドさせます。
  • circlesIntersect() 関数で2つの円が交差しているかどうかをチェックします。

このスケッチを実行すると、次のような結果が得られます:

  • 100個の円が画面上をゆっくりと動きます。
  • 円同士が交差すると、その瞬間に赤い点が表示されます(瞬間的な交差)。
  • 交差した場所に薄い赤い点が残り、時間とともに蓄積されていきます(集積的な交差)。

円のサイズ、速度、数、交差の表示方法などのパラメータを調整することで、様々な視覚効果を得ることができます。

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


この写真を模写するp5.jsコードを生成してください。キャンバスサイズは800×800とします。



この写真を模写するためのp5.jsコードを生成します。写真に含まれる形状や色合いを簡略化し、自然な緑の葉と分岐した茎の形状を再現することを目指します。

以下が、その基本的なp5.jsコードです。キャンバスサイズは800×800に設定しています。

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 関数を使用して、再帰的に分岐する茎を描いています。これは、実際の植物の複雑な構造を模倣するための基本的なアルゴリズムです。

このコードを元に、より複雑な形や色のバリエーションを加えることで、模写の精度を高めることができます。





この写真を模写するp5.jsコードを生成してください。キャンバスサイズは800×800とします。


アップロードされた画像から、らせん状に伸びる植物の茎を模倣したp5.jsコードの簡単な例を示します。葉と背景は単純化し、抽象的なデザインで巻き付いたつるを表現します。ニーズに応じて、さらに洗練させることができます。

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つの単純化された葉の形を描きます。
- 三角関数を使用して描画されたループ状の曲線で、らせん状のつるを表現しています。
- 背景は写真と同じような薄い緑色。

このコードをp5.jsエディタ(editor.p5js.orgなど)にコピー&ペーストして、結果を見ることができます。あなたのデザインに合うようにさらにカスタマイズしたり、具体的な変更を要求したりするのは自由です!







メタ・プログラミングと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


生成規則(7回目)

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(自動機械と人間)

  • A History of AI and Generative Art
  • MAY 29 - OCTOBER 15, 2019
  • Kate Vass Galerie, Switzerland

自動機械と人間 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はディープラーニングを利用した配色ジェネレーターです。写真、映画、ポピュラーアートからカラースタイルを学習することができます。



風景を描くようになったきっかけ:

風景画のレッスン:形・構図・色の基礎知識 / ミッチェル・アルバラ