GODRICH
#css애니메이션#버튼#로딩#전환효과#스크롤#대시보드#실전ui#디자인트렌드

로렌츠 어트랙터 9종 — 나비 모양 카오스 궤적을 미적분 한 걸음씩 계산해 쌓는 캔버스

미분방정식 세 개를 한 걸음씩 적분해 점의 자리를 옮기면 나비 날개 모양 궤적이 저절로 쌓입니다. 라이브러리 없이 복붙으로 가져가는 카오스 시각화입니다.

자동 재생 · 눌러서 해당 항목으로 · 전부 한 zip

데모

아홉 판 각각은 html 파일 하나로 자립해 열자마자 움직이고, 화면의 모든 변화는 CSS 애니메이션이 아니라 미분방정식을 다시 적분하거나 누적한 결과입니다. 24프레임 녹화 구간에서 아홉 중 여덟 판은 23프레임이 움직였고 04 어트랙터 지도책만 19프레임인데, 색 채널 세 장을 순차 등장시키는 구조상 잠시 멈추는 프레임이 있는 것이 정상입니다. 08 은 포인터로 계수를 비틀 수 있고 02 · 05 · 09 는 회전하는 입체 장면이니 지나가기 전에 멈춰 보시기를 권합니다.

24프레임 실측 한눈에

항목 초점 누적면적(%) 강도 평균
01 나비 기본 세 방정식을 오일러로 한 걸음씩 4.882 51.6
02 3D 회전 투영 이방 스케일로 두 변을 채우는 사선 투영 28.715 102.4
03 RK4 적분 두 적분기를 나란히 견주는 정밀 검증 11.656 51.9
04 어트랙터 지도책 클리포드·드용 점화식의 확률 꽃 8.677 15.5
05 아이자와·토머스 다른 방정식이 주는 구면과 나선 0.768 60.7
06 밀도 구름 방문 횟수를 안개로 쌓는 히스토그램 1.04 72.7
07 푸앵카레 단면 평면 통과 순간만 찍는 점 구름 3.425 25.9
08 파라미터 손잡이 포인터로 시그마·로를 비트는 탐험 9.233 37.6
09 나비 히어로 씬 대문에 바로 쓰는 조립판 10.849 67.5

수치는 성격으로 읽는다

누적면적은 02 3D 회전 투영이 28.715% 로 가장 넓게 채우고 강도평균 102.4 로도 가장 밝으며, 03 RK4 11.656% · 09 히어로 씬 10.849% · 08 파라미터 손잡이 9.233% 가 뒤따릅니다. 반대편의 작은 값들은 결함이 아니라 재료의 성격입니다 — 05 아이자와·토머스(0.768%)는 굵은 채움이 아니라 얇은 궤적 선 한 가닥으로 형태를 보여주고, 06 밀도 구름(1.04%)은 방문 횟수를 안개처럼 희박하게 쌓는 히스토그램이며, 07 푸앵카레 단면(3.425%)은 기준면을 통과하는 순간만 찍는 점 구름이라 원래 화면을 덮는 면적이 작습니다. 01(4.882%)도 한 가닥 궤적 선이 순서대로 그려지는 화면이라 같은 성격이고, 선을 굵히거나 점을 번지게 해서 수치를 부풀리면 각 판의 정체가 사라집니다.

오일러 적분 한 겹에서 로렌츠 나비를 몇 겹으로 다시 읽는 순서

순서는 화려함의 서열이 아니라 하나의 흐름을 몇 겹으로 다시 읽어 내려가는가의 서열입니다. 먼저 세 방정식을 오일러 적분 한 겹으로 기본 나비를 얹고(01), 공간축으로 돌려 입체로 읽으며(02), 시간축의 정밀도를 올립니다(03). 넷째와 다섯째는 세계 자체를 바꾸는 두 걸음 — 미분방정식이 아니라 2차원 점화식의 지도책을 펼치고(04), 아예 다른 계의 어트랙터와 나란히 견줍니다(05). 여섯째부터는 관점의 전환인데, 한 점의 궤적 대신 수천 점의 통계로 읽고(06), 3차원 흐름을 2차원 지도로 접은 뒤(07), 계산을 손에 쥐어 주고(08), 마지막에 그 모든 부품을 서비스의 첫 화면으로 조립합니다(09). 매 프레임 중력을 적분하는 천체 수천 개의 상호중력을 프레임마다 굽는 은하 충돌과 같은 계보이고, 확산 속도 차이가 저절로 무늬를 빚는 반응 확산·발산 횟수를 세어 색을 매기는 만델브로 줌과도 이웃하는, 계산으로 그림을 얻는 편입니다.

항목별 코드

01나비 기본 — 세 개의 미분방정식을 오일러로 한 걸음씩

나비 기본 — 세 개의 미분방정식을 오일러로 한 걸음씩
새 창에서 열기

01 · 나비 기본 — 세 개의 미분방정식을 오일러로 한 걸음씩 — 측정 누적면적 4.882% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

CanvasRenderingContext2DrequestAnimationFramelineTo

σ=10 · ρ=28 · β=8/3 의 계수로 dx/dt = σ(y−x) · dy/dt = x(ρ−z)−y · dz/dt = xy−βz 를 오일러 dt 0.01 로 3,000걸음 미리 적분해 두고, 2초 주기로 궤적을 순서대로 공개하면(공개가 끝나는 위상 0.82, 이후 페이드아웃) 두 날개를 오가는 나비가 저절로 그려집니다. 궤적을 24구간으로 나눠 구간별 평균 y 로 깊이 음영을 주고 머리 점이 앞장서는데, 측정은 누적면적 4.882% · 강도평균 51.6 입니다.

세 방정식과 사전 적분 루프

  function deriv(x, y, z) {
    return [SIG * (y - x), x * (RHO - z) - y, x * y - BETA * z];
  }

  // 궤적 사전 계산 — 과도기 200걸음을 건너뛰어 어트랙터 위에서 시작한다
  var xs = new Float32Array(STEPS), ys = new Float32Array(STEPS), zs = new Float32Array(STEPS);
  var x = 0.6, y = 0.7, z = 21, i;
  for (i = 0; i < 200; i++) {
    var d0 = deriv(x, y, z);
    x += d0[0] * DT; y += d0[1] * DT; z += d0[2] * DT;
  }
  for (i = 0; i < STEPS; i++) {
    xs[i] = x; ys[i] = y; zs[i] = z;
    var d1 = deriv(x, y, z);
    x += d1[0] * DT; y += d1[1] * DT; z += d1[2] * DT;
  }

023D 회전 투영 — 궤적을 사선 투영으로 빙글빙글 돌린다

3D 회전 투영 — 궤적을 사선 투영으로 빙글빙글 돌린다
새 창에서 열기

02 · 3D 회전 투영 — 궤적을 사선 투영으로 빙글빙글 돌린다 — 측정 누적면적 28.715% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

requestAnimationFrameMath.sinMath.cos

z축을 2초 주기로 돌리고 시야를 14초 주기로 0.10~1.00라디안 사이에서 기울여 z 를 가로 성분에도 섞는 사선 투영이라서, 어떤 회전각에서도 궤적이 실 한 줄로 무너지지 않고 26구간 깊이 음영이 입혀집니다. 캔버스는 2.4:1 로 넓은데 어트랙터는 1:1 에 가까워 가로 W/57 · 세로 H/37.5 의 이방 스케일로 두 변을 각각 채우는 것이 이 판의 열쇠인데, 이 누적면적 28.715% 는 아홉 중 최대이고 강도평균 102.4 역시 최대입니다.

이방 스케일 사선 투영 함수

    var a1 = tSec / SPIN_S * 6.2832;    // z축 회전각(라디안) — 나비가 도는 빠른 축(2초)
    var a2 = Math.sin(tSec / SPIN_L * 6.2832) * 0.45 + 0.55;  // 시야 기울기(0.10~1.00rad 왕복, 14초) — 0 을 지나지 않는다
    var ca1 = Math.cos(a1), sa1 = Math.sin(a1);
    var ca2 = Math.cos(a2), sa2 = Math.sin(a2);

    // 이방 스케일 — 캔버스는 2.4:1 넓은 창, 어트랙터는 1:1 에 가까워 한 변으로 묶으면
    // 가로가 절반 이상 비는 것을 막는다. 가로 W/57 · 세로 H/37.5 로 각 변을 채운다.
    var sx = W / 57, sy = H / 37.5;
    var ox = W / 2, oy = H / 2;

    // 사선 투영 — z축 회전(u, w) 뒤 기울기 a2 로 z 를 가로에도 섞는다.
    // 가로 성분이 두 축에 걸려 있어 어떤 회전각에서도 궤적이 실 한 줄로 무너지지 않는다.
    function pr(i) {
      var u = xs[i] * ca1 - ys[i] * sa1;
      var w = xs[i] * sa1 + ys[i] * ca1;
      var zv = zs[i] - 25;
      return [ox + (u * ca2 + zv * sa2) * sx, oy - (zv * ca2 * 0.66 + w * 0.40) * sy];
    }

03RK4 적분 — 오일러에서 4차 룽게-쿠타로 정밀도를 올린다

RK4 적분 — 오일러에서 4차 룽게-쿠타로 정밀도를 올린다
새 창에서 열기

03 · RK4 적분 — 오일러에서 4차 룽게-쿠타로 정밀도를 올린다 — 측정 누적면적 11.656% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

requestAnimationFramelineToarc(

같은 dt 0.02 로 왼쪽에는 오일러 · 오른쪽에는 RK4 를 나란히 2,400걸음 적분하면, 오일러 쪽은 몇 바퀴 뒤 형태가 일그러지고 RK4 쪽은 유지됩니다. 2초 주기로 궤적을 공개하고 위상 0.84 이후 페이드하는 화면의 측정은 누적면적 11.656% · 강도평균 51.9 입니다.

오일러와 RK4 한 걸음

  // 오일러 한 걸음 — 기울기 한 번으로 위치를 옮긴다
  function euler(p) {
    var d = deriv(p[0], p[1], p[2]);
    return [p[0] + d[0] * DT, p[1] + d[1] * DT, p[2] + d[2] * DT];
  }
  // RK4 한 걸음 — k1~k4 네 기울기의 가중 평균(1/6, 1/3, 1/3, 1/6)
  function rk4(p) {
    var k1 = deriv(p[0], p[1], p[2]);
    var k2 = deriv(p[0] + k1[0] * DT / 2, p[1] + k1[1] * DT / 2, p[2] + k1[2] * DT / 2);
    var k3 = deriv(p[0] + k2[0] * DT / 2, p[1] + k2[1] * DT / 2, p[2] + k2[2] * DT / 2);
    var k4 = deriv(p[0] + k3[0] * DT, p[1] + k3[1] * DT, p[2] + k3[2] * DT);
    return [
      p[0] + DT / 6 * (k1[0] + 2 * k2[0] + 2 * k3[0] + k4[0]),
      p[1] + DT / 6 * (k1[1] + 2 * k2[1] + 2 * k3[1] + k4[1]),
      p[2] + DT / 6 * (k1[2] + 2 * k2[2] + 2 * k3[2] + k4[2])
    ];
  }

04어트랙터 지도책 — 클리포드·드용, 수백만 점으로 그리는 확률 꽃

어트랙터 지도책 — 클리포드·드용, 수백만 점으로 그리는 확률 꽃
새 창에서 열기

04 · 어트랙터 지도책 — 클리포드·드용, 수백만 점으로 그리는 확률 꽃 — 측정 누적면적 8.677% · 움직인 프레임 19/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

requestAnimationFrameMath.sinfillRect

미분방정식이 아닌 2차원 점화식 — 클리포드 x′=sin(a·y)+c·cos(a·x) · y′=sin(b·x)+d·cos(b·y), 드용 x′=sin(a·y)−b·cos(x) · y′=sin(c·x)−d·cos(y) — 의 계수 조합 6벌(클리포드 3 · 드용 3)을 시드 고정 난수로 채널당 2만 번 반복해 방문 카운트를 150px 누적 버퍼에 쌓고, 3색 채널을 2초 주기로 순차 등장시킵니다. 처음 400걸음 과도기는 버려 문양을 또렷하게 하는데, 측정은 누적면적 8.677% · 강도평균 15.5 · 움직인프레임 19(순차 등장 구조상 멈추는 프레임이 있음)입니다.

한 채널을 쌓는 누적 루프

  function accumulate(set, upto) {
    var rand = rng(set.kind * 1000 + 7);
    var x = rand() * 2 - 1, y = rand() * 2 - 1;
    var n = Math.floor(ITERS * upto);
    var a = set.p[0], b = set.p[1], c = set.p[2], d = set.p[3];
    var i, px, py;
    for (i = 0; i < n + 400; i++) {
      if (set.kind === 0) {
        var nx = Math.sin(a * y) + c * Math.cos(a * x);
        y = Math.sin(b * x) + d * Math.cos(b * y);
        x = nx;
      } else {
        var nx2 = Math.sin(a * y) - b * Math.cos(x);
        y = Math.sin(c * x) - d * Math.cos(y);
        x = nx2;
      }
      if (i < 400) continue;              // 과도기 버림
      px = (x + 3) / 6 * (RES - 1);
      py = (y + 3) / 6 * (RES - 1);
      if (px >= 0 && px < RES && py >= 0 && py < RES) {
        acc[(py | 0) * RES + (px | 0)] += 1;
      }
    }
  }

05아이자와·토머스 — 구면과 나선, 다른 방정식이 주는 다른 조각

아이자와·토머스 — 구면과 나선, 다른 방정식이 주는 다른 조각
새 창에서 열기

05 · 아이자와·토머스 — 구면과 나선, 다른 방정식이 주는 다른 조각 — 측정 누적면적 0.768% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

requestAnimationFrameMath.sinlineTo

로렌츠가 아니어도 어트랙터는 무궁무진하다는 비교 실험으로, 왼쪽 아이자와는 구면을 감도는 궤적 · 오른쪽 토머스는 실뭉치 나선을 그립니다. 둘 다 RK4 dt 0.02 로 1,800걸음 적분하고 공개 2초 · 관찰축 회전 12초로 입체 형태를 보여주는데, 얇은 궤적 선 한 가닥으로 형태를 보여주는 성격이라 누적면적 0.768% · 강도평균 60.7 의 작은 면적이 자연스럽습니다.

아이자와와 토머스의 속도식

  function dAizawa(x, y, z) {
    var a = 0.95, b = 0.7, c = 0.6, d = 3.5, e = 0.25, f = 0.1;
    return [
      (z - b) * x - d * y,
      d * x + (z - b) * y,
      c + a * z - z * z * z / 3 - (x * x + y * y) * (1 + e * z) + f * z * x * x * x
    ];
  }
  // 토머스 계수 b=0.19 — 주기적으로 겹치는 나선
  function dThomas(x, y, z) {
    var b = 0.19;
    return [Math.sin(y) - b * x, Math.sin(z) - b * y, Math.sin(x) - b * z];
  }

06밀도 구름 — 방문 횟수를 픽셀 밝기로 쌓는 안개 질감

밀도 구름 — 방문 횟수를 픽셀 밝기로 쌓는 안개 질감
새 창에서 열기

06 · 밀도 구름 — 방문 횟수를 픽셀 밝기로 쌓는 안개 질감 — 측정 누적면적 1.04% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

requestAnimationFramefillRectMath.min

한 점이 아니라 시드 42 로 뿌린 2,000점을 동시에 풀어 오일러 dt 0.016 · 프레임당 2걸음씩 진행하고, xz 투영의 방문 횟수를 150px 히스토그램에 누적하면 궤적 선이 안개 같은 밀도 장으로 바뀝니다. 2초 주기로 리셋한 뒤 다시 쌓는데, 히스토그램의 희박함이 성격이라 누적면적 1.04% · 강도평균 72.7 입니다.

2,000점을 동시에 밀어 주는 루프

  function stepAll() {
    var i, k, dx, dy, dz;
    for (k = 0; k < 2; k++) {
      for (i = 0; i < NPTS; i++) {
        var x = px[i], y = py[i], z = pz[i];
        dx = SIG * (y - x); dy = x * (RHO - z) - y; dz = x * y - BETA * z;
        px[i] = x + dx * DT; py[i] = y + dy * DT; pz[i] = z + dz * DT;
      }
    }
  }

07푸앵카레 단면 — 궤적이 평면을 지나는 순간만 찍는다

푸앵카레 단면 — 궤적이 평면을 지나는 순간만 찍는다
새 창에서 열기

07 · 푸앵카레 단면 — 궤적이 평면을 지나는 순간만 찍는다 — 측정 누적면적 3.425% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

requestAnimationFramelineToMath.min

3차원 흐름을 2차원 지도로 접는 고전 기법의 화면 이식으로, RK4 dt 0.005 로 촘촘히 14,000걸음(총 70 시간단위)을 적분하며 이전 z 와 현재 z 가 기준면 z=27 을 건넌 순간을 선형 보간해 (x, y) 에만 점을 찍습니다. 상승 통과는 얼음색 · 하강 통과는 복숭아색으로 나눠 각 날개에 띠가 자라고 통과마다 3×3 가우시안 근사 스플랫을 150px 지도에 누적하는데, 통과 순간만 찍는 점 구름이라 누적면적 3.425% · 강도평균 25.9 입니다.

z 27 건넘의 선형 보간

  // 통과 목록 — z 가 27 을 건너는 순간의 보간 (x, y) 와 방향(1 상승 / 0 하강)
  var crX = [], crY = [], crUp = [];
  for (i = 1; i < STEPS; i++) {
    var za = zs[i - 1] - Z_PLANE, zb = zs[i] - Z_PLANE;
    if ((za < 0 && zb >= 0) || (za >= 0 && zb < 0)) {
      var t2 = za / (za - zb);
      crX.push(xs[i - 1] + (xs[i] - xs[i - 1]) * t2);
      crY.push(ys[i - 1] + (ys[i] - ys[i - 1]) * t2);
      crUp.push(zb > za ? 1 : 0);
    }
  }

08파라미터 손잡이 — 포인터로 시그마·로를 비틀면 형태가 변한다

파라미터 손잡이 — 포인터로 시그마·로를 비틀면 형태가 변한다
새 창에서 열기

08 · 파라미터 손잡이 — 포인터로 시그마·로를 비틀면 형태가 변한다 — 측정 누적면적 9.233% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

pointermoverequestAnimationFrameCanvasRenderingContext2D

포인터 x 로 σ(4~24) · y 로 ρ(10~45) 를 정하고 두 날개 축을 원주 12분할로 감싼 시드 12개의 궤적을 매 프레임 다시 적분해 보여주는 계수 탐험 판입니다. 계수는 즉시 반영하지 않고 τ 0.06초(60ms) 추종 지연으로 다가가서, 손을 움직이면 나비가 찌그러지고 뒤집히며 새 궤도로 태어나는데 측정은 누적면적 9.233% · 강도평균 37.6 입니다.

계수를 물고 매 프레임 다시 그리는 궤적

  function traceOrbit(sx, sy, sz, sigv, rhov) {
    var x = sx, y = sy, z = sz, i;
    for (i = 0; i < 60; i++) {           // 과도기
      var dx0 = sigv * (y - x), dy0 = x * (rhov - z) - y, dz0 = x * y - BETA * z;
      x += dx0 * DT; y += dy0 * DT; z += dz0 * DT;
    }
    for (i = 0; i < STEPS; i++) {
      bufX[i] = x; bufZ[i] = z;
      var dx = sigv * (y - x), dy = x * (rhov - z) - y, dz = x * y - BETA * z;
      x += dx * DT; y += dy * DT; z += dz * DT;
      if (x > 900 || x < -900 || z > 900 || z < -900) {  // 발산 가드
        for (var i2 = i + 1; i2 < STEPS; i2++) { bufX[i2] = x; bufZ[i2] = z; }
        return i;
      }
    }
    return STEPS;
  }

09나비 히어로 씬 — 글자·입자와 겹쳐 쓰는 대문 장면

나비 히어로 씬 — 글자·입자와 겹쳐 쓰는 대문 장면
새 창에서 열기

09 · 나비 히어로 씬 — 글자·입자와 겹쳐 쓰는 대문 장면 — 측정 누적면적 10.849% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

requestAnimationFramecreateRadialGradientMath.sin

06 의 밀도 구름(2,400점 · 오일러 dt 0.016)을 배경에 깔고 관찰각을 30초에 한 바퀴 돌리며, 미리 적분한 은하 팔 궤적의 밝은 머리가 구름 위를 12초에 걸쳐 흐릅니다. 비네팅으로 가장자리를 누르고 세 줄 글자(CHAOS BUTTERFLY · 로렌츠 어트랙터 시뮬레이터 · dz/dt = xy − βz)를 얹어 대문 장면을 완성하는데, 측정은 누적면적 10.849% · 강도평균 67.5 입니다.

관찰각 회전을 히스토그램에 누적

  // 회전 각도에 맞춰 xz 히스토그램에 누적 — 회전은 x/y 축 섞기로 표현
  function accumulate(a) {
    var ca = Math.cos(a), sa = Math.sin(a);
    var i, u, v, gx, gy;
    for (i = 0; i < NPTS; i++) {
      u = px[i] * ca - py[i] * sa;
      v = px[i] * sa + py[i] * ca;
      gx = (u / 22 + 0.5) * (RES - 1);
      gy = (1 - (pz[i] - 1) / 47) * (RES - 1);
      if (gx >= 0 && gx < RES && gy >= 0 && gy < RES) {
        hist[(gy | 0) * RES + (gx | 0)] += 1;
      }
    }
  }

오일러 dt 를 0.05 이상 올리면 나비가 깨지는 함정

아홉 판을 실제로 만들며 반복해서 걸린 지점은 네 갈래로 좁혀졌습니다 — 오일러 걸음 폭을 너무 크게 잡는 경우, 정사각 스케일을 그대로 쓰는 경우, 단면 통과를 프레임 단위로 검사하는 경우, 그리고 시드 없는 난수를 쓰는 경우입니다. 네 갈래 모두 코드는 에러 없이 돌아가고 화면에도 무언가가 그려지는데, 결과물만 의도한 그림과 달라진다는 공통점이 있어 처음 붙여 볼 때 가장 자주 걸리며, 아래 넷을 순서대로 짚어 두면 대부분 피할 수 있습니다.

오일러 dt 를 크게 잡으면 나비가 무너진다

오일러의 한 걸음 오차는 걸음마다 쌓이므로 dt 를 0.05 이상으로 올리면 궤적이 바깥으로 뻗어나가 두 날개의 균형이 깨집니다. 이 편의 오일러 판들은 01 · 02 · 06 · 09 처럼 혼자 쓰일 때 dt 0.01~0.016 구간에 두었고, dt 를 키우는 실험은 03 처럼 오일러와 RK4 를 나란히 두고 견주는 화면에서 하는 것이 안전합니다.

정사각 스케일은 가로를 절반 비게 한다

02 의 스케일을 min(W, H) 한 변으로만 잡으면 캔버스는 2.4:1 로 넓은데 어트랙터는 1:1 에 가까워 가로가 절반 이상 빕니다. 가로 W/57 · 세로 H/37.5 의 이방 스케일이 누적면적 28.715% 를 만든 열쇠인데, 같은 계산을 하고도 스케일 한 줄로 그림의 밀도가 배로 달라지는 셈입니다.

단면 통과를 프레임 단위로 검사하면 점이 어긋난다

07 은 dt 0.005 로 촘촘히 적분하지만 프레임이 아니라 걸음 사이의 z 변화를 보고, 건넘 순간을 이전 점과 현재 점의 선형 보간으로 좁혀 (x, y) 를 얻습니다. 보간 없이 건넌 걸음의 좌표를 그대로 찍으면 점이 실제 통과 자리에서 멀어져 띠가 번지므로, 보간이 이 지도의 정확도를 결정합니다.

Math.random 은 재생 결정론을 깨뜨린다

04 · 06 · 09 가 시드를 고정한 mulberry32 류 난수를 쓰는 것은 같은 시각의 프레임이 언제나 같은 그림이어야 하기 때문인데, Math.random 을 쓰면 렌더마다 다른 문양이 나와 재생이 결정론을 잃습니다. 아홉 판의 index.html 을 그대로 담은 암호 zip 의 비밀번호는 32hdtxzf 이니, 풀면 본문의 코드 조각과 줄 단위로 나란히 대조해 보실 수 있습니다.

세 변형 중 하나가 그대로 접근성 대응으로 이어지는 이유

값 하나를 바꾼 세 변형

이름 바꾼 값 느낌
시간 스텝 01 오일러 dt 0.01 → 0.005 걸음을 반으로 줄여 궤적이 부드러워지고 같은 시간을 채우는 걸음 수가 두 배가 된다
계수 로 08 시그마 10 · 로 28 → 로 35 나비 날개가 더 벌어지며 궤적이 바깥으로 달아나는 주기가 눈에 띄게 짧아진다
점 개수 06 밀도 구름 동시 점 2,000 → 8,000 안개가 짙어지며 날개 내부 구조가 하늘의 은하처럼 밝아진다

세 변형 모두 판의 구조는 그대로 두고 상수 한 곳만 고쳐 얻습니다. 걸음을 줄이면 정밀과 비용의 관계를 손으로 확인할 수 있고, ρ 를 올리면 흐름의 종류가 바뀌는 것이 한눈에 들어오며, 점을 늘리면 통계 장이 짙어져 같은 식의 다른 얼굴이 나옵니다.

동작을 줄인 사용자에게 주는 장면

각 판은 시작할 때 prefers-reduced-motion: reduce 일치 여부를 검사해, 켜져 있으면 애니메이션 루프를 돌리지 않고 쌓인 대표 장면 한 번을 그려 고정합니다. 정지 사용자에게도 빈 캔버스가 아니라 나비 한 장이 전달되는 것이 목적입니다.

손으로 만지는 판과 화면 낭독

08 만 pointermove 로 σ 와 ρ 를 받고 나머지 판은 자동 재생이며, 모든 실험판에는 사람이 알 수 있는 aria-label 과 tabindex 가 붙고 dt · 걸음 수 · 주기 같은 측정 메타가 화면 아래에 함께 표시됩니다. 04 · 06 · 09 의 난수는 시드 고정이라 같은 시각의 장면이 재현됩니다.

이런 화면에서 쓴다 — 나비 히어로와 파라미터 놀이터

아홉 판 중 실제로 화면에 바로 얹을 수 있는 조합은 셋으로 좁혀집니다. 09 나비 히어로 씬은 글자와 입자를 겹쳐 쓰는 대문 장면이라 제품 소개 페이지의 첫 화면이나 강의 사이트의 인트로에 그대로 옮겨 쓸 수 있고, 배경이 어두운 히어로 영역에서 특히 눈에 띕니다.

08 파라미터 손잡이는 포인터로 시그마·로 값을 직접 비틀어 형태가 바뀌는 판이라, 인터랙티브 데모 페이지에서 방문자가 계수를 만져 보게 하는 용도로 맞습니다. 값이 손잡이 범위를 벗어나지 않도록 08 자체의 발산 가드가 이미 걸려 있어 별도 예외 처리 없이 붙여 쓸 수 있습니다.

06 밀도 구름은 방문 횟수를 밝기로 쌓아 안개처럼 보이는 질감이라, 카드 배경이나 섹션 구분선 뒤에 옅게 깔아 두는 용도로 쓰기 좋습니다. 점 수를 낮추면 계산량이 줄어 배경 용도에 맞는 가벼운 판이 됩니다.

로렌츠 어트랙터를 처음 만질 때 자주 나오는 질문 FAQ

카오스 이론 뜻을 이 나비 하나로 설명할 수 있나요?

가능합니다. 카오스 이론의 뼈대는 결정론 규칙과 초기값 민감성의 공존인데, 이 편의 나비가 정확히 그 두 가지를 보여 줍니다. 세 방정식은 같은 입력에 같은 출력을 주는 결정론이고, 08 에서 계수를 살짝 비틀었을 때 궤적이 전혀 다른 길로 갈라지는 것이 민감성이니, 이 두 장면만으로 뜻을 전달할 수 있습니다.

로렌츠 어트랙터 코드를 수학 배경 없이 따라할 수 있나요?

가능합니다. 코드의 미분방정식은 실수 세 개(x, y, z)를 매 걸음 조금씩 옮기는 덧셈·곱셈으로 풀려 있어 미적분을 몰라도 줄 단위로 따라갈 수 있고, zip 안에는 이 판들을 같은 조건으로 다시 짜라고 시키는 지시 문서가 들어 있어 코드 도구에 붙여 넣으면 같은 화면부터 시작합니다. 그다음은 상수 블록의 σ · ρ · β · dt 값을 하나씩 바꿔 보는 것이 가장 빠른 학습 순서입니다.

계수를 어디까지 바꿔도 화면이 안전한가요?

08 의 손잡이 범위인 σ 4~24 · ρ 10~45 라면 언제든 안전합니다. 이 바깥의 극단값에서는 궤적이 발산해 점이 화면 밖으로 달아날 수 있는데, 08 의 traceOrbit 이 좌표가 ±900 을 넘으면 이후 걸음을 그 자리에 묶는 발산 가드를 두었으니 참고해서 자기 판에 같은 안전장치를 넣으면 됩니다.

다시 보면 — 왜 같은 식인데 매번 다른 길이 나오는가

초기값 민감성이 그림을 지배한다

세 방정식은 완전히 결정론적이라 같은 초기값에서는 언제나 같은 궤적이 나오지만, 초기값에 아주 작은 차이를 심으면 그 차이가 걸음마다 커져 얼마 지나지 않아 전혀 다른 길을 걷습니다. 카오스 이론의 핵심인 이 초기값 민감성은 08 에서 손으로 확인할 수 있는데, 계수를 살짝 비틀 때마다 열두 궤적이 한데 엉키다가 다시 제각각 갈라서는 것이 같은 식 안에서 일어나는 일입니다. 06 의 2,000점도 같은 원리로 흩어지는데, 처음에는 뭉쳐 있던 점 구름이 어트랙터의 두 날개를 따라 늘어나 안개가 되는 것은 각 점의 작은 시작 차이가 시간에 걸쳐 벌어진 결과입니다.

왜 ρ 를 올리면 나비가 달라지나

σ=10 · β=8/3 을 고정하고 ρ 만 움직이면 궤적의 성격이 단계적으로 바뀌는데, ρ 가 낮을 때는 흐름이 한곳에 가라앉고 ρ 28 부근에서 두 날개를 끝없이 오가는 흐름이 나타납니다. 08 은 이 관계를 ρ 10~45 범위의 손잡이로 살려 두었으니, ρ 를 위로 밀수록 날개가 더 벌어지고 바깥으로 달아나는 주기가 눈에 띄게 짧아지는 것을 직접 볼 수 있습니다. 계수는 그림의 장식이 아니라 흐름의 종류를 고르는 스위치입니다.

왜 적분기에 따라 오차가 다른가

오일러는 한 걸음을 출발점 기울기 하나로 계산해 오차가 dt 에 1차 비례해 걸음마다 쌓이지만, RK4 는 네 번의 기울기를 1/6 · 1/3 · 1/3 · 1/6 가중치로 평균 내 오차가 dt 의 네제곱에 비례합니다. 같은 dt 0.02 라도 자릿수 단위로 오차가 벌어지므로 03 의 두 판은 시간이 흐를수록 모양이 갈라지는데, 이것이 정밀 시뮬레이션에 RK4 를 쓰는 이유이자 2,000점 이상을 매 프레임 다시 적분하는 구름 판이 오일러에 작은 dt 를 택해 프레임 예산을 맞추는 이유입니다.

더 읽을거리

압축 비밀번호를 입력하세요

비밀번호는 이 글 본문 안에 있습니다. 읽다 보면 나옵니다.