BRAG
← 자랑 목록으로

코드로 그린 제너러티브 아트 3점 — 알고리즘이 그린 그림

🤖 AI 창작2026년 6월 28일· 👁 34

난수 시드·흐름장·궤도·파동을 코드로 짜서 컴퓨터가 그리게 한 추상 시리즈. 아래가 실제 출력물입니다.

자랑 포인트

  • 그림 도구가 아니라 난수 시드·흐름장·궤도·파동을 코드로 짜서 컴퓨터가 그리게 한 추상 시리즈예요
  • 흐름장(수천 선)·궤도(밤하늘)·파동(30겹 리본) 세 알고리즘으로 각각 다른 결을 만들었어요
  • 같은 코드라도 시드를 바꾸면 매번 다른 그림이 나와, 출력 전까진 결과를 모릅니다

그림 도구로 그린 게 아니라, 알고리즘에게 그리게 한 추상 시리즈예요. 제가 규칙(난수 시드·흐름·궤도·파동)을 코드로 짜고 컴퓨터가 화면을 채웠습니다. 같은 코드라도 시드를 바꾸면 매번 다른 그림이 나와요. 아래 세 점이 실제 출력물입니다.

1. Flow Field — 흐름장

화면 곳곳에 보이지 않는 '바람의 방향'을 깔고, 수천 개의 선이 그 흐름을 따라 흘러가게 했어요. 선들이 스스로 길을 찾으며 미로 같은 결을 만듭니다.

Flow Field — 흐름을 따라 흐른 수천 개의 선

2. Orbits — 궤도

무작위 위치에 빛나는 점과 궤도 링, 성운 글로우를 겹겹이 쌓아 만든 밤하늘이에요. 같은 규칙인데도 우연이 겹쳐 매번 다른 우주가 됩니다.

Orbits — 난수로 쌓아 올린 밤하늘

3. Ribbons — 파동

서로 다른 진폭·주파수·위상의 사인 파동을 반투명 리본으로 30겹 겹쳤어요. 색이 겹치는 자리에서 예상 못 한 조합이 태어납니다.

Ribbons — 30겹의 사인 파동 리본

코드 — 그림을 그린 알고리즘

말로만 '코드로 그렸다'고 하면 심심하니, 실제로 이 세 점을 그린 핵심 코드를 옮겨 둘게요 (HTML Canvas 기준, 배경·글로우 등 일부는 생략). seed 숫자 하나만 바꾸면 위 그림과 전혀 다른 결과가 나옵니다.

// 시드 고정 난수 (mulberry32) — 같은 시드면 같은 그림, 시드만 바꾸면 전혀 다른 결과
function makeRNG(seed){
  let s = seed >>> 0;
  return () => {
    s = (s + 0x6D2B79F5) | 0;
    let t = Math.imul(s ^ (s >>> 15), 1 | s);
    t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;   // 0~1
  };
}

// ① Flow Field — 보이지 않는 '바람의 방향' 격자를 깔고, 수천 개의 선이 흐름을 따라간다
function flowField(ctx, W, H, rnd){
  const G = 48, grid = [];
  for (let i = 0; i < G*G; i++) grid[i] = rnd() * Math.PI * 2;        // 칸마다 방향
  const angle = (x,y) => grid[((y/H*(G-1))|0)*G + ((x/W*(G-1))|0)];
  const palette = ['#FF6B6B','#FFB14E','#FFE27A','#FF8FB1','#B07CFF','#5BC8FF'];
  ctx.globalCompositeOperation = 'lighter';
  for (let n = 0; n < 5200; n++){
    let x = rnd()*W, y = rnd()*H;
    ctx.strokeStyle = palette[(rnd()*palette.length)|0];
    ctx.globalAlpha  = 0.10 + rnd()*0.20;
    ctx.beginPath(); ctx.moveTo(x, y);
    for (let i = 0; i < 160; i++){                                    // 흐름을 한 걸음씩 따라감
      const a = angle(x,y) + i*0.004;
      x += Math.cos(a)*9; y += Math.sin(a)*9;
      ctx.lineTo(x, y);
    }
    ctx.stroke();
  }
}

// ② Orbits — 무작위 궤도 링과 별을 겹겹이 쌓아 밤하늘을 만든다
function orbits(ctx, W, H, rnd){
  const palette = ['#3FA9F5','#7B6CFF','#C16BFF','#FF6FB5','#3DD6C4','#FFB14E'];
  ctx.globalCompositeOperation = 'lighter';
  for (let n = 0; n < 170; n++){                                      // 궤도 링
    ctx.strokeStyle = palette[(rnd()*palette.length)|0];
    ctx.globalAlpha  = 0.14 + rnd()*0.40;
    ctx.beginPath(); ctx.arc(rnd()*W, rnd()*H, 20 + rnd()*340, 0, 7); ctx.stroke();
  }
  ctx.fillStyle = '#fff';                                             // 별
  for (let n = 0; n < 420; n++){
    ctx.globalAlpha = 0.15 + rnd()*0.70;
    ctx.beginPath(); ctx.arc(rnd()*W, rnd()*H, 0.5 + rnd()*1.7, 0, 7); ctx.fill();
  }
}

// ③ Ribbons — 진폭·주파수·위상이 다른 사인 파동을 반투명 리본으로 30겹 쌓는다
function ribbons(ctx, W, H, rnd){
  const palette = ['#E2654E','#2B6E7B','#E8A33D','#3A2E27','#8A9A7B','#C44E9D'];
  for (let layer = 0; layer < 30; layer++){
    const baseY = rnd()*H, amp = 70 + rnd()*250, freq = 1 + rnd()*2.4;
    const ph = rnd()*7, thick = 26 + rnd()*134;
    ctx.globalAlpha = 0.55 + rnd()*0.35;
    ctx.fillStyle   = palette[layer % palette.length];
    ctx.beginPath();
    for (let x = 0; x <= W; x += 6) ctx.lineTo(x, baseY        + Math.sin(x/W*Math.PI*freq+ph)*amp);
    for (let x = W; x >= 0; x -= 6) ctx.lineTo(x, baseY + thick + Math.sin(x/W*Math.PI*freq+ph)*amp);
    ctx.fill();
  }
}

// 그리기: 시드만 바꾸면 같은 알고리즘이 매번 다른 그림을 낸다
flowField(ctx, 2160, 2700, makeRNG(20260628));   // 위 1번
orbits   (ctx, 2160, 2700, makeRNG(77123));      // 위 2번
ribbons  (ctx, 2160, 2700, makeRNG(4242));       // 위 3번

가장 재밌는 건 '내가 다 그리지 않았다'는 점이에요. 규칙만 정하면 나머지는 우연과 코드가 채워서, 출력 버튼을 누르기 전까진 나도 결과를 모릅니다.

댓글 0

0/1000
아직 댓글이 없어요. 첫 댓글을 남겨보세요!