コードを書くことと、アートを作ること。この2つは長い間、別々の世界の話だと思っていました。
プログラムを書くのはロジックで、アートを作るのは感性。そんな分け方が自分の中にあって、どちらかに集中しようとするたびに、もう一方が引っかかっていました。
Antigravity と出会って、その感覚が少し変わりました。コードの意図を伝えると、AIが実装してくれる。アイデアをスケッチするように書くと、Three.jsのシーンが立ち上がる。「この動きをもっと有機的にしたい」と言葉で伝えると、Perlinノイズのパラメータが調整される。
この記事は、そんな体験を具体的な実装として共有するものです。p5.jsでジェネレーティブアートを始めて、Three.jsで3D表現に踏み込み、GLSLシェーダーで表現を深めるまでのプロセスを、Antigravityとのやりとりを含めながら書いています。
クリエイティブコーディングは、完成させることよりも、実験のプロセス自体が面白い分野です。ただ、それを言い訳にして動かないコードを抱えたままにするのも違う。ここで扱うのは「動くもの」を作ることにこだわって書きました。
環境構築:p5.js + Antigravity のセットアップ
Vite + p5.js プロジェクトを立ち上げる
クリエイティブコーディングの環境は、シンプルであるほど続きます。p5.jsをCDNで読み込む方法もありますが、Viteを使うとホットリロードが効いて、コードを変えるたびにリアルタイムで結果が反映されます。この体験は、アート制作のフローと相性がいいです。
# Vite プロジェクトを作成
npm create vite@latest my-generative-art -- --template vanilla
cd my-generative-art
npm install
npm install p5
# Antigravity でプロジェクトを開く
# antigravity . でもいいし、GUIから開いてもOK
main.jsを以下のように書き換えます。p5.jsをインスタンスモードで使うのがポイントです。グローバルモードだと変数汚染が起きやすく、複数のスケッチを管理するときに問題が出ます。
// main.js — p5.js インスタンスモードのベース設定
import p5 from 'p5'
const sketch = (p) => {
let canvas
p.setup = () => {
canvas = p.createCanvas(p.windowWidth, p.windowHeight)
canvas.parent('app')
p.colorMode(p.HSB, 360, 100, 100, 100)
p.background(0, 0, 10)
}
p.draw = () => {
// ここにジェネレーティブアートのロジックを書く
}
p.windowResized = () => {
p.resizeCanvas(p.windowWidth, p.windowHeight)
}
}
new p5(sketch)
Antigravity のプロジェクトルール設定
クリエイティブコーディングのプロジェクトでは、Antigravityに「このプロジェクトはアート制作用で、パフォーマンスより視覚的な豊かさを優先する」という方向性を伝えておくと、提案の質が変わります。.antigravity/rules に以下のようなルールを書いておきます。
# Creative Coding Project Rules
このプロジェクトはジェネレーティブアート・インタラクティブアートの制作用です。
## 優先事項
- 視覚的な面白さ > コードの簡潔さ
- 実験的なアプローチを歓迎する
- アニメーションのタイミングや色彩は「感性的な判断」を含む提案を推奨する
- パラメータは数値ハードコードより const で命名することを好む
## 技術スタック
- フロントエンド: Vite + p5.js または Three.js
- シェーダー: GLSL(WebGL)
- デプロイ: Cloudflare Pages
## コードスタイル
- コメントは日本語でOK
- マジックナンバーには必ず意味のある名前をつける
- パラメータはファイル先頭にまとめて定義する
このルールファイルがあると、Antigravityの提案が「アート制作者の視点」になります。同じ「ノイズを追加して」というプロンプトでも、返ってくるコードの質が変わります。
ジェネレーティブアートの基本パターン:ノイズ・粒子・軌跡
Perlin ノイズで「生きている」フローを描く
ジェネレーティブアートで最も汎用的なツールの一つが、Perlin ノイズです。ランダムに見えるけれど、実際には滑らかにつながっている値の流れを生成できます。これを使うと、自然物の動き——煙、水、風——に近いものが作れます。
Antigravity に「Perlin ノイズを使ったフローフィールドで粒子が流れるアニメーションを作りたい。色は青と紫のグラデーションにして、粒子は薄く残像を残す感じにしたい」と伝えると、以下のようなコードを提案してきます。自分ではパラメータの意味を全部理解してなくても、「このTIME_SCALEの値を変えると何が変わるか」と聞くと丁寧に説明してくれます。
// フローフィールド + パーティクル — p5.js 実装
import p5 from 'p5'
const sketch = (p) => {
// ============ パラメータ(調整用) ============
const PARTICLE_COUNT = 800 // 粒子の数
const NOISE_SCALE = 0.003 // ノイズの細かさ(小さいほど滑らか)
const TIME_SCALE = 0.0005 // 時間変化の速さ
const TRAIL_ALPHA = 8 // 残像の濃さ(0-255、小さいほど長く残る)
const SPEED = 2.5 // 粒子の速度
const HUE_RANGE = [200, 290] // 色相の範囲(青〜紫)
let particles = []
let time = 0
class Particle {
constructor() {
this.reset()
}
reset() {
this.x = p.random(p.width)
this.y = p.random(p.height)
this.hue = p.random(HUE_RANGE[0], HUE_RANGE[1])
this.alpha = p.random(60, 100)
this.size = p.random(1, 3)
this.life = 0
this.maxLife = p.random(150, 400)
}
update() {
// ノイズから角度を計算
const noiseVal = p.noise(
this.x * NOISE_SCALE,
this.y * NOISE_SCALE,
time
)
const angle = noiseVal * p.TWO_PI * 2
// 角度に沿って移動
this.x += p.cos(angle) * SPEED
this.y += p.sin(angle) * SPEED
this.life++
// 画面外 or 寿命が尽きたらリセット
if (
this.x < 0 || this.x > p.width ||
this.y < 0 || this.y > p.height ||
this.life > this.maxLife
) {
this.reset()
}
}
draw() {
// 寿命に応じてフェードアウト
const lifeRatio = 1 - this.life / this.maxLife
p.stroke(this.hue, 80, 90, this.alpha * lifeRatio)
p.strokeWeight(this.size)
p.point(this.x, this.y)
}
}
p.setup = () => {
p.createCanvas(p.windowWidth, p.windowHeight).parent('app')
p.colorMode(p.HSB, 360, 100, 100, 100)
p.background(240, 30, 8)
// 粒子を初期化
for (let i = 0; i < PARTICLE_COUNT; i++) {
particles.push(new Particle())
}
}
p.draw = () => {
// 背景を薄く塗り直すことで残像効果を作る
p.fill(240, 30, 8, TRAIL_ALPHA)
p.noStroke()
p.rect(0, 0, p.width, p.height)
// 全粒子を更新・描画
particles.forEach(particle => {
particle.update()
particle.draw()
})
time += TIME_SCALE
}
p.windowResized = () => {
p.resizeCanvas(p.windowWidth, p.windowHeight)
}
// クリックで粒子をリセット
p.mousePressed = () => {
particles.forEach(p => p.reset())
}
}
new p5(sketch)
このコードを動かすと、青から紫に変化する粒子が画面全体をゆっくり流れるアニメーションが生成されます。NOISE_SCALEを大きくすると流れが細かくランダムになり、小さくすると大きなうねりになります。この数値を変えながら「自分の感じる気持ちよさ」を探すのが、ジェネレーティブアートの醍醐味の一つです。
パーティクルシステムのインタラクション
マウスやタッチに反応するインタラクションを加えると、作品が「見るもの」から「触れるもの」に変わります。Antigravity に「マウスカーソルの位置に引き寄せられる力を加えて、クリックで反発するようにしたい」と伝えると、物理シミュレーションを含むコードを提案してくれます。
実装の鍵は、引力と反発力のバランスです。力が強すぎると粒子がすべてカーソルに集まってしまい、弱すぎると効果がわかりにくい。Antigravity に「引力の強さを調整しているんだけど、なんかピンとこありません。もっと『ゆらゆら集まる』感じにしたい」と具体的な感覚で伝えると、減衰係数(damping)の調整や、力の計算式の変更を提案してくれます。
// マウスとのインタラクションを追加したパーティクルシステム
class InteractiveParticle {
constructor() {
this.x = p.random(p.width)
this.y = p.random(p.height)
this.vx = 0 // 速度x
this.vy = 0 // 速度y
this.hue = p.random(180, 300)
}
update() {
const dx = p.mouseX - this.x
const dy = p.mouseY - this.y
const dist = p.sqrt(dx * dx + dy * dy)
// 距離に応じた引力(近すぎると弱くなる)
const ATTRACT_STRENGTH = 0.3
const MIN_DIST = 50
if (dist > MIN_DIST) {
this.vx += (dx / dist) * ATTRACT_STRENGTH
this.vy += (dy / dist) * ATTRACT_STRENGTH
}
// クリック中は反発
if (p.mouseIsPressed) {
this.vx -= (dx / dist) * 1.5
this.vy -= (dy / dist) * 1.5
}
// 減衰(これがないと粒子が発散し続ける)
this.vx *= 0.95
this.vy *= 0.95
this.x += this.vx
this.y += this.vy
// 画面端で折り返す
if (this.x < 0) this.x = p.width
if (this.x > p.width) this.x = 0
if (this.y < 0) this.y = p.height
if (this.y > p.height) this.y = 0
}
draw() {
const speed = p.sqrt(this.vx * this.vx + this.vy * this.vy)
// 速度に応じて明るさを変える
p.fill(this.hue, 80, p.map(speed, 0, 5, 60, 100), 80)
p.noStroke()
p.ellipse(this.x, this.y, p.map(speed, 0, 5, 2, 6))
}
}
Three.js への移行:3D 表現の扉を開く
p5.jsで2Dの動きに慣れてきたら、Three.jsで3D表現に踏み込んでみましょう。最初は「カメラ」「ライト」「マテリアル」という概念が増えて混乱しますが、Antigravityを使うと「3Dの直方体を回転させて、ライトを斜め上から当てたい」というような感覚的な指示で実装してもらえます。
Three.js シーンの基本構成
Three.jsのシーンは「Scene(舞台)」「Camera(カメラ)」「Renderer(カメラが捉えた映像を画面に出すもの)」の3要素で成り立っています。Antigravity にこの設計を任せると、WebGLのコンテキスト管理やrequestAnimationFrameのループも含めてきれいに書いてくれます。
npm install three @types/three
// Three.js シーンの基本セットアップ
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
// ============ シーン初期化 ============
const scene = new THREE.Scene()
scene.background = new THREE.Color(0x0a0a14)
// カメラ(視野角75度、画面のアスペクト比、近クリップ/遠クリップ)
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
)
camera.position.set(0, 0, 5)
// レンダラー(アンチエイリアスON)
const renderer = new THREE.WebGLRenderer({
antialias: true,
powerPreference: 'high-performance'
})
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) // Retinaは最大2倍まで
document.body.appendChild(renderer.domElement)
// マウスでカメラを操作できるコントロール
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true // 慣性を有効化(動きが滑らかになる)
// ============ ジオメトリとマテリアル ============
// Icosahedronは球に近い多面体。ジェネレーティブアートに使いやすい
const geometry = new THREE.IcosahedronGeometry(1.5, 1)
const material = new THREE.MeshPhongMaterial({
color: 0x6644aa,
emissive: 0x220044,
wireframe: true, // ワイヤーフレームで構造を見せる
transparent: true,
opacity: 0.8
})
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)
// ライティング
const ambientLight = new THREE.AmbientLight(0xffffff, 0.3)
scene.add(ambientLight)
const pointLight = new THREE.PointLight(0x8866ff, 2, 20)
pointLight.position.set(3, 3, 3)
scene.add(pointLight)
// ============ アニメーションループ ============
let time = 0
function animate() {
requestAnimationFrame(animate)
time += 0.01
// 頂点をノイズで動かす(Antigravityに「有機的な変形」と伝えて実装してもらった)
const positions = geometry.attributes.position
for (let i = 0; i < positions.count; i++) {
const x = positions.getX(i)
const y = positions.getY(i)
const z = positions.getZ(i)
// 単純な正弦波による変形(本来はSimplexNoiseを使うとより有機的)
const offset = Math.sin(x * 2 + time) * 0.15 + Math.cos(y * 2 + time * 0.7) * 0.1
const len = Math.sqrt(x * x + y * y + z * z)
const scale = (1.5 + offset) / len
positions.setXYZ(i, x * scale, y * scale, z * scale)
}
positions.needsUpdate = true
geometry.computeVertexNormals()
mesh.rotation.y += 0.003
mesh.rotation.x += 0.001
controls.update()
renderer.render(scene, camera)
}
animate()
// リサイズ対応
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth, window.innerHeight)
})
このコードは、ワイヤーフレームの多面体が有機的に変形しながら回転するシーンを生成します。wireframe: trueをfalseに変えて、マテリアルをMeshPhongMaterialからMeshStandardMaterialに変えると、質感のある立体が現れます。
Antigravity との3D設計の進め方
Three.jsで「自分がやりたいこと」を実現するとき、Antigravity への伝え方で得られる結果が大きく変わります。経験上、以下のパターンが効果的です。
感覚ベースで伝える(推奨): 「星雲のような、光が集まったり散ったりする表現がしたい」
参照ベースで伝える: 「このCodePenのような動き(URLを貼る)をThree.jsで実装したい」
問題ベースで伝える: 「頂点シェーダーで変形しているとき、法線が正しく更新されなくて陰影がおかしくなります。どう直せばいい?」
特に「感覚ベース」の伝え方は、自分でも言語化できていないビジョンをAntigravityが補完してくれる感覚があります。
GLSL シェーダーを AI と書く
Three.jsのStandardMaterialやPhongMaterialで表現できない効果を実現したいとき、GLSLシェーダーを書きます。これは多くのクリエイティブコーダーが「難しい」と感じる壁ですが、Antigravityを使うと、アルゴリズムの意図を日本語で伝えながら実装できます。
シェーダーには「頂点シェーダー(Vertex Shader)」と「フラグメントシェーダー(Fragment Shader)」があります。頂点シェーダーは3D空間の頂点位置を変換し、フラグメントシェーダーはピクセルの色を決定します。
フラグメントシェーダーで宇宙的なノイズ表現を作る
Antigravityに「GLSL の fract と sin を組み合わせた、宇宙の星雲のようなノイズ表現を、Three.jsのShaderMaterialで使えるように実装してほしい」と伝えると、以下のようなコードを提案してきます。
// fragmentShader.glsl — ノイズベースの宇宙的表現
// Three.js の ShaderMaterial に渡す GLSL コード
uniform float uTime;
uniform vec2 uResolution;
varying vec2 vUv; // 頂点シェーダーから受け取る UV 座標
// 2D のハッシュ関数(疑似乱数)
vec2 hash22(vec2 p) {
p = vec2(dot(p, vec2(127.1, 311.7)),
dot(p, vec2(269.5, 183.3)));
return -1.0 + 2.0 * fract(sin(p) * 43758.5453123);
}
// 2D の Gradient Noise(Perlin ノイズに似た滑らかなノイズ)
float noise(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
// Hermite 補間(滑らかな遷移)
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(
mix(dot(hash22(i + vec2(0.0, 0.0)), f - vec2(0.0, 0.0)),
dot(hash22(i + vec2(1.0, 0.0)), f - vec2(1.0, 0.0)), u.x),
mix(dot(hash22(i + vec2(0.0, 1.0)), f - vec2(0.0, 1.0)),
dot(hash22(i + vec2(1.0, 1.0)), f - vec2(1.0, 1.0)), u.x),
u.y
);
}
// フラクタルブラウン運動(FBM)— ノイズを重ねて複雑な模様を作る
float fbm(vec2 p) {
float value = 0.0;
float amplitude = 0.5;
float frequency = 1.0;
// 5オクターブ分のノイズを重ねる
for (int i = 0; i < 5; i++) {
value += amplitude * noise(p * frequency);
frequency *= 2.0; // 周波数を倍に(細かさが増す)
amplitude *= 0.5; // 振幅を半分に(影響が弱まる)
}
return value;
}
void main() {
vec2 uv = vUv * 3.0; // UV をスケールして模様の大きさを調整
// 時間とともにゆっくり動くノイズ
float n = fbm(uv + uTime * 0.1);
float n2 = fbm(uv * 1.5 + vec2(n, n) + uTime * 0.07);
// 色の計算:青から紫、白への変化
vec3 colorA = vec3(0.1, 0.1, 0.4); // 深い青
vec3 colorB = vec3(0.5, 0.2, 0.8); // 紫
vec3 colorC = vec3(0.9, 0.8, 1.0); // 白みがかった光
vec3 color = mix(colorA, colorB, n);
color = mix(color, colorC, pow(n2, 3.0)); // 明るい部分を際立たせる
// ビネット効果(端が暗くなる)
float vignette = 1.0 - length(vUv - 0.5) * 1.5;
color *= max(0.0, vignette);
gl_FragColor = vec4(color, 1.0);
}
// Three.js で ShaderMaterial を使う実装
const shaderMaterial = new THREE.ShaderMaterial({
uniforms: {
uTime: { value: 0 },
uResolution: {
value: new THREE.Vector2(window.innerWidth, window.innerHeight)
}
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: fragmentShaderCode, // 上記のGLSLコードを文字列として渡す
})
// アニメーションループでtimeを更新
function animate() {
requestAnimationFrame(animate)
shaderMaterial.uniforms.uTime.value += 0.01
renderer.render(scene, camera)
}
このシェーダーのコードをAntigravityが提案してくれたとき、「fbmって何をしているの?」と聞いたら、「フラクタルブラウン運動(Fractional Brownian Motion)といって、同じノイズ関数を異なるスケールで重ねることで、自然界の複雑な模様を再現する手法です。山の地形や雲の形がこれで説明できます」と教えてくれました。こういう理解が積み重なって、次第に自分でシェーダーを変形できるようになります。
よくある落とし穴と Antigravity による解決
クリエイティブコーディングで詰まりやすいポイントを、Antigravityがどう助けてくれるかとともに紹介します。
落とし穴1:WebGL コンテキスト喪失
ブラウザのタブを長時間バックグラウンドにしていたり、GPUリソースが逼迫したりすると、WebGLのコンテキストが失われます。この問題は最初は気づきにくく、「なんか真っ暗になった」という症状として現れます。
Antigravityに「WebGLのコンテキストロストを検知して、自動的にシーンを復元する処理を追加したい」と伝えると、以下のような解決策を提示してくれます。
// コンテキスト喪失への対応
const canvas = renderer.domElement
canvas.addEventListener('webglcontextlost', (event) => {
event.preventDefault()
console.warn('WebGL context lost. Pausing animation.')
cancelAnimationFrame(animationId)
}, false)
canvas.addEventListener('webglcontextrestored', () => {
console.log('WebGL context restored. Reinitializing.')
// シーンを再初期化する
initScene()
animate()
}, false)
落とし穴2:モバイルでの devicePixelRatio によるパフォーマンス問題
Retina ディスプレイで renderer.setPixelRatio(window.devicePixelRatio) をそのまま使うと、iPhone などでは devicePixelRatio が3になり、描画するピクセル数が9倍になります。粒子が多いジェネレーティブアートでは、これが原因でフレームレートが大幅に落ちます。
// パフォーマンスを考慮したピクセル比の設定
// 最大2に抑えることでRetinaの恩恵を受けつつ負荷を下げる
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
落とし穴3:頂点変形後の法線ベクトル不更新
Three.jsで頂点シェーダーや JavaScript で頂点位置を変形すると、陰影計算に使う「法線ベクトル」が古いままになり、ライティングがおかしくなります。
// 頂点を変形した後は必ず法線を再計算する
positions.needsUpdate = true
geometry.computeVertexNormals() // これを忘れがち!
Antigravityに「変形した3Dオブジェクトにライトを当てると陰影が変にグラデーションされてしまう」と伝えると、このcomputeVertexNormals()の呼び忘れを即座に指摘してくれます。エラーメッセージが出ないだけに気づきにくいバグです。
落とし穴4:p5.js の frameRate() で見えないパフォーマンス問題
p5.jsのスケッチが重くなってきたとき、frameRate() の値を確認するのが最初のステップです。ただし、パフォーマンス問題の原因は複数あります。
// setup に追加してフレームレートを表示
p.setup = () => {
// ... 通常の初期化
p.createP('').id('fps-display')
}
// draw の最後に追加
p.draw = () => {
// ... 描画処理
document.getElementById('fps-display').innerHTML =
`FPS: ${p.frameRate().toFixed(1)}`
}
Antigravityに「FPSが30以下に落ちているんだけど、どこがボトルネックか調べたい」と伝えると、Chromeの DevTools Performance パネルの使い方から、p5.jsのpreload()活用、描画の最適化パターンまで、段階的にアドバイスをくれます。
作品を届ける:展示・公開・収益化
クリエイティブコーディングの作品を作ったら、世界に届けましょう。Antigravityは展示プラットフォームの技術的な要件(iframe埋め込み、インタラクション対応など)も把握しているので、各プラットフォームへの最適化も相談できます。
GitHub Pages + Cloudflare Pages での公開
最も手軽な公開方法は、GitHub Pages または Cloudflare Pages です。Viteでビルドして、静的ファイルをデプロイするだけで、URLが発行されます。
# ビルド
npm run build
# Cloudflare Pages へデプロイ(wranglerが必要)
npx wrangler pages deploy dist --project-name my-generative-art
Antigravityに「Cloudflare PagesのCI/CDを設定したい。GitHubにプッシュしたら自動でデプロイされるようにしたい」と伝えると、.github/workflows/deploy.yml の設定から Cloudflare の API トークンの取得方法まで、一気通貫で教えてくれます。
OpenProcessing での展示
OpenProcessing は p5.js のスケッチを公開・共有できるプラットフォームです。コードをそのままブラウザで実行でき、他のアーティストのフィードバックを受けられます。作品のコードを public で公開することで、クリエイティブコーディングコミュニティとのつながりを作れます。
デジタル商品としての販売
完成したジェネレーティブアートを、静止画・動画・インタラクティブ体験として販売することができます。
- 静止画: 作品のスクリーンショットをHigh-res(4K以上)でエクスポートして、Adobe Stock・BOOTH・BASE で販売
- プリント販売: Redbubble・SUZURI でのグッズ展開
- カスタムコミッション: ブランドや個人向けのオーダーメイドジェネレーティブアート
p5.jsでHigh-resエクスポートする実装を Antigravity に依頼すると、p5.Graphics を使ったオフスクリーン描画の方法を教えてくれます。
// 高解像度エクスポートのための実装
p.keyPressed = () => {
if (p.key === 's' || p.key === 'S') {
// 4K解像度でオフスクリーンに描画
const exportGraphics = p.createGraphics(3840, 2160)
drawArtwork(exportGraphics, 3840, 2160) // 描画ロジックを関数化しておく
p.save(exportGraphics, `artwork_${Date.now()}.png`)
exportGraphics.remove()
}
}
個人開発者の視点から(実体験メモ)
AI と共創するアートの哲学
最後に、Antigravity とクリエイティブコーディングを続けてきて感じることを書いておきます。
AIを使うことで「自分でコードを書いた」と言えるのか、という問いを持つ人は多いと思います。私もそう感じた時期がありました。
でも今は、少し違う見方をしています。画家がキャンバスを使うこと、写真家がカメラを使うことと同じように、Antigravityはクリエイティブコーディングのための「道具」です。違うのは、この道具が「対話」を通じて使えるという点です。
「もっと有機的にして」「色が強すぎる」「このリズムを速くして」——こういう言葉で作品を育てていく体験は、思ったより創造的です。コードの細部を知らなくても、方向性を持ってフィードバックを続けることで、自分のスタイルが作品に宿っていきます。
逆に言えば、Antigravityを使うほど「何を作りたいか」を考える時間が増えます。実装の悩みから解放されると、表現の悩みに集中できます。それはアーティストとして、とても豊かな変化だと感じています。
まずは、p5.jsで小さなスケッチを一つ作ってみてください。「動けばいい」レベルで十分です。そこからAntigravityと対話しながら、少しずつ育てていく体験を、ぜひ試してみてください。