← 캐러셀
캐러셀 · 타이포
미술관 캡션
스타일 가이드 · 편차 있음
ChatGPT 이미지 생성 결과는 매번 조금씩 다릅니다. 글자·레이아웃이 정확해야 하면 코드 렌더 레시피를 고르세요.
흰 벽 받침대 위 사물과 작은 캡션 카드로 전시하세요.
1장1 / 5 · 6초 반복
가상 브랜드 하얀 물레 공방의 모션 예시입니다. 번호나 화살표로 1~5장을 모두 재생해 보세요.
프롬프트 보기15,271자
# 내 캐러셀 (여기만 채운다)
브랜드명:
브랜드 색 (바탕 / 글자 / 포인트, 모르면 비워 둔다):
주제:
넣을 글자 (한 줄에 한 장, 역할은 / 로 나눈다, 한 칸 안에서 줄을 바꾸려면 //):
1장 (제목 / 보조):
2장 (소제목 / 본문):
3장 (소제목 / 본문):
4장 (소제목 / 본문):
5장 (한 줄):
예 — 채우는 형식만 보여 준다. 위 칸 대신 그대로 쓰지 않는다.
- 브랜드명: 하얀 물레 공방
- 브랜드 색: #EFEDE8 / #2B2B2B / #6B8F71
- 주제: 손으로 빚은 그릇 오래 쓰는 법
- 1장: 손으로 빚은 그릇 / 오래 쓰는 세 가지 방법
- 2장: 첫 사용 전 쌀뜨물 / 쌀뜨물에 한 번 끓여 두면 // 작은 틈이 메워져요
- 3장: 식기세척기는 피해요 / 센 물살에 유약이 상해요 // 부드러운 수세미로 닦아요
- 4장: 완전히 말려 두기 / 엎어서 하루 말린 뒤 // 찬장에 넣어요
- 5장: 매일 쓰는 그릇 하나
## 이 칸을 쓰는 규칙
- 아래 본문의 《브랜드명》《주제》《바탕색》《글자색》《포인트색》은 위 칸의 값이다. 《1장 제목》《2~4장 본문》처럼 장과 역할이 붙은 자리는 위 넣을 글자에서 그 장 줄의 그 역할 칸이다(2~4장은 장마다 자기 줄). 《 》 이름 자체는 화면에 쓰지 않는다.
- 위 칸 대신 이 메시지 끝이나 다음 메시지로 알려 줘도 같다 — 그 값을 위 칸에 채운 것으로 본다.
- 칸이 비어 있으면 만들지 말고 빈 칸만 한 번에 물어본다. 대화나 기억에 내 브랜드 정보가 있으면 브랜드명·색·주제는 그것으로 채우고 확인만 받는다. 넣을 글자가 비어 있으면 주제에 맞춰 분량 안에서 5장 문구를 지어 먼저 보여 주고, 내가 좋다고 하면 만든다. 브랜드 색을 모르면 본문의 기본색을 쓴다.
- 내가 "예시대로"라고 할 때만 위 예 값을 쓴다(그때는 "그림 먼저 만들기"를 건너뛰고 "에셋" 주소를 그대로 쓴다). 그 밖에는 예 값을 한 글자도 쓰지 않는다.
- 장을 넘길 때마다(내가 "다음"이라고 할 때마다) 위 칸에서 그 장 줄을 다시 읽어 그대로 쓴다. 예 값이나 앞 장 글자로 돌아가지 않는다.
- 괄호 안 설명은 화면에 넣지 않는다. 브랜드명이 "향기서랍 (을지로 향초 공방)"이면 화면에는 "향기서랍"만 쓴다.
# 먼저 확인
- 이건 인스타그램 캐러셀 5장을 움직이는 영상으로 만들기 위한 HTML 파일이다. 그림이 아니라 코드로 만든다. 파일 하나(museum-label.html)에 다섯 장을 모두 넣는다.
- 다섯 장 모두 아래 "모든 장 공통" 규칙을 똑같이 지켜서, 나란히 놓으면 한 벌로 보이게 한다.
- 넣을 글자 분량: 1장 제목 16자 · 보조 24자 / 2~4장 소제목 16자 · 본문 3줄·줄마다 14자 / 5장 한 줄 16자. 한도를 넘으면 만들지 말고 줄여 달라고 해.
- 이 스타일은 글자 없는 전시실 사진 5장(s1.jpg ~ s5.jpg)이 필요하다. 아래 "먼저 전시실 사진 5장 만들기"를 먼저 확인해.
- 이미지 파일이 없고 내가 새로 만들 수 없다고 하면, 아래 "에셋" 섹션의 예시 주소를 그대로 써서 만든다.
# 이 캐러셀
브랜드는 《브랜드명》, 주제는 《주제》. 스타일은 "미술관 캡션" — 흰 벽 받침대 위 사물과 작은 캡션 카드로 전시하세요. 배경은 내가 준 전시실 사진 5장(s1.jpg ~ s5.jpg, 글자 없음)이고, 그 위에 벽 제목·캡션 카드를 코드로 얹어 움직인다. 전시 설치처럼 조용하게: 읽는 순서대로 벽 글자가 한 자씩 붙고 → 캡션 카드가 벽에 걸리고 → 카드 글줄이 올라오고 → 핵심 낱말이 초록으로 칠해진다. 3.4초 넘게 읽히는 동안 시점이 사물 쪽으로 아주 천천히 다가가고(사진 밝기는 그대로), 끝에서 글과 카드가 한꺼번에 빠진다.
# 먼저 전시실 사진 5장 만들기 (코드보다 먼저)
- 이 스타일은 장마다 배경 사진이 하나씩 필요하다(s1.jpg ~ s5.jpg, 세로 4:5). 내가 첨부했거나 파일 이름을 알려 줬는지 확인하고, 없으면 먼저 달라고 해.
- 3장처럼 관리법을 말하는 장은 사물 하나에 그 동작이 보이게(예: 그릇 안에 부드러운 수세미가 걸쳐 있음) — 1장과 같은 구도라도 넘길 때 구별되게 한다.
- 사진이 없으면 Duchamp 의 정적 "미술관 캡션" 이미지 템플릿으로 먼저 만들고, 이미지 도구에 이렇게 덧붙인다: "same scene, but remove ALL text: no wall title, no caption card, no line on the floor — leave the wall clean and empty."
- 다섯 장 모두 같은 전시실·같은 조명·같은 카메라 높이. 흰 받침대는 판면 가로 36~42% 지점에 서고(너비 약 34%), 사물 하나만 놓인다. 받침대 오른쪽 벽은 캡션 카드 자리라 비워 둔다. 5장은 1~4장과 같은 받침대(같은 자리·높이·너비)에 결말다운 큰 그릇 하나 — 그릇 폭이 받침대 윗면과 거의 같게(판면 너비 약 36%). 5장만 받침대를 작게 하거나 시점을 바꾸지 않는다(한 벌 규칙).
- 사진 안에 글자·카드·액자·조명 기구·사람이 보이면 다시 만든다. 글자는 모두 코드에서 넣는다.
# 모든 장 공통
## 층·카드·선 (이 CSS 를 글자 그대로 쓴다 — 말로 풀어 다시 만들지 않는다)
```css
.stage{position:relative;width:1080px;height:1350px;overflow:hidden;background:var(--c1);color:var(--c2);isolation:isolate}
.room{position:absolute;inset:0;background-size:124% auto;background-position:-150px -5px}
.spot{position:absolute;width:900px;height:1100px;margin:-550px 0 0 -450px;border-radius:50%;pointer-events:none;
background:radial-gradient(closest-side,rgba(255,252,244,.75),rgba(255,252,244,.28) 45%,transparent 100%);mix-blend-mode:soft-light}
.pool{position:absolute;width:620px;height:760px;margin:-380px 0 0 -310px;border-radius:50%;pointer-events:none;
background:radial-gradient(closest-side,rgba(255,246,228,.34),rgba(255,246,228,.12) 55%,transparent);mix-blend-mode:screen}
.sweep{position:absolute;top:-200px;left:0;width:360px;height:1750px;pointer-events:none;
background:linear-gradient(90deg,transparent,rgba(255,250,238,.55) 50%,transparent);mix-blend-mode:soft-light;transform-origin:50% 50%}
.shade{position:absolute;inset:0;background:radial-gradient(120% 90% at 40% 45%,transparent 55%,rgba(40,36,30,.16));pointer-events:none}
.wall{position:absolute;left:76px;right:76px;font-weight:500;letter-spacing:-.01em;line-height:1.18;color:var(--c2)}
.wall .ch{display:inline-block;white-space:pre}
.t1{font-size:74px} .t2{font-size:62px} .t5{font-size:68px}
.card{position:absolute;width:400px;background:color-mix(in srgb,#fff 88%,var(--c1));padding:30px 30px 28px 32px;
box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--c2) 13%,transparent)}
.card .band{position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--c3);transform-origin:50% 0}
.card .ln{display:block;overflow:hidden;padding:.03em 0 .12em} .card .ln>span{display:block}
.card .body{white-space:nowrap;font-size:34px;font-weight:400;letter-spacing:-.015em;line-height:1.45}
.card .no{font-size:26px;font-weight:400;letter-spacing:.02em;color:color-mix(in srgb,var(--c2) 84%,transparent);margin-top:24px}
.card .brand{font-size:32px;font-weight:700;letter-spacing:-.01em;white-space:nowrap}
.k{font-style:normal;position:relative;display:inline-block;font-weight:600}
.k1{font-style:normal;position:absolute;left:0;top:0;white-space:pre;color:color-mix(in srgb,var(--c3) 78%,#000);clip-path:inset(0 100% 0 0)}
```
- 겹침 순서(뒤 → 앞, 문서 순서 그대로, z-index 없음): .room(사진) → .spot(천장 조명) → .pool(빛 웅덩이) → .sweep(빛 스치기) → .shade(가장자리 그늘) → .wall(벽 제목) → .card(안에 글줄들, 맨 끝에 .band).
- 사진은 1.24배로 잘라 쓴다(위 .room 의 background-size·position). 받침대 아래 끝은 화면 밖으로 나가고, 사물이 커지며 화면 세로 가운데 쪽(윗변 약 y 480)으로 내려온다. 바닥에 선을 긋지 않는다.
- .room 의 transform-origin 은 장마다 사물 위치(아래 장별 배치의 '초점').
- 벽 제목은 글자마다 <span class="ch"> 로 나눈다(띄어쓰기도 한 칸). 낱말 중간에서 줄이 끊기지 않게 낱말 하나를 white-space:nowrap 인 span 으로 한 번 더 묶고, // 자리에는 <br> 을 넣는다.
## 글꼴
- Pretendard Variable. 벽 제목은 다섯 장 모두 500·자간 -0.01em(크기로만 위계: 1장 74 · 5장 68 · 2~4장 62px — 캡션 카드와 대비가 나되 벽을 누르지 않게), 자간 0.01em — 벽에 붙인 비닐 글자처럼. 카드 글은 위 CSS 그대로.
## 캡션 카드
- 너비 400px(5장만 262px), 모서리 직각, 그림자 없이 1px 옅은 테두리만(실제 전시 캡션처럼). 1~4장 카드 안은 위에서부터: 첫 칸(1장 보조 · 2~4장 본문 — 모두 .body 34px 400, 줄바꿈 없이 한 줄씩) → 24px → 전시 번호 '《브랜드명》 · 01' 꼴(26px). 5장은 1~4장과 같은 두 칸: 《브랜드명》(.brand 32px 700) → 24px → 전시 번호 '05' 만(브랜드명이 두 번 나오지 않게). 왼쪽 --c3 세로 띠(.band, 3px, 카드 높이 전체)는 카드의 마지막 자식.
## 색
- 모든 글자는 --c2. --c3 는 카드 띠, 그리고 1~4장 카드 첫 칸의 핵심 낱말 하나에만(짙게 섞어서). 벽과 바닥 톤은 사진의 것.
- 브랜드 색을 비워 두었으면 --c1 #F5F4F0, --c2 #1C1C1C, --c3 #9A8F7D 를 쓴다.
# 장별 배치
모든 좌표는 판면 왼쪽 위 기준 px. 벽 제목·카드 모두 **위에서부터** 쌓는다(top 이 윗변). 벽 제목은 left 76 · right 76, 왼쪽 정렬.
- 장마다 값(1.14배로 자른 뒤 좌표. 초점 = .room transform-origin · 조명·웅덩이 = 타원 중심 left, top · 카드 = left, top):
1장 초점 29% 47% · 조명 315, 640 · 웅덩이 315, 580 · 카드 636, 530
2장 초점 30% 49% · 조명 330, 665 · 웅덩이 330, 605 · 카드 636, 536
3장·4장 1장과 같음 · 5장 1장과 같고 카드만 764, 530, 너비 262 (그릇 오른쪽 끝과 80px 넘게 띄운다)
## 1장 · 표지
- 벽 제목(.t1, 74px, 500) top 222(사물 쪽으로 내려 빈 벽을 줄인다), 최대 2줄. 카드 첫 칸 .body(34px, 400)에 보조 — 그 안의 핵심 낱말 하나(샘플 '세 가지')도 2~4장처럼 초록 강조 → 24px → 전시 번호.
## 2~4장 · 본문
- 소제목(.t2, 62px, 500) top 232, 최대 2줄. 카드 첫 칸에 본문(.body, 34px, 줄마다 14자, 3줄까지) → 24px → 전시 번호 02·03·04.
- 받침대 위 사물만 장마다 바뀐다(사진). 카드 자리는 위 값 그대로.
## 5장 · 마지막 장
- 한 줄(.t5, 68px, 500) top 226, 최대 2줄. 카드(764, 530, 너비 262 — 그릇이 커진 만큼 오른쪽, 그릇 테두리와 80px 이상)에 《브랜드명》(.brand 32px 700) → 24px → '05'(.no).
- 받침대 위 사물은 1장과 같은 유약의 그릇 하나 — 결말 장이니 크게(판면 너비 약 36%).
# 움직임 (한 장 6000ms, 24fps 로 144프레임)
박자 상수 (완성 화면 = 0ms): OUT = 3400, OUT_DUR = 450, GONE = OUT + OUT_DUR = 3850, IN = 4300, 글자 간격 CH = 35, CARD_IN = IN + 600 = 4900, BAND_IN = IN + 650 = 4950, LINE_IN = IN + 850 = 5150, IN_DUR = 600, STAG = 80 (ms).
읽는 순서: 벽 제목 한 자씩(4300~) → 캡션 카드 바탕(4900~)·초록 세로선(4950~) → 카드 글줄(5150~) → 핵심 낱말 칠하기(다음 바퀴 150~650). **퇴장은 모두 같은 시각(3400~3850)에 opacity 0 + translateY(-10px) — 퇴장에는 stagger 를 쓰지 않는다.** 3850~4300 은 사진만 보이는 빈 틈이다.
- 전시실 사진 (slow dolly, transform-origin = 초점, 밝기는 바꾸지 않는다): 0 scale(1.00978) (linear) → 3400 scale(1.04) (ease-inout) → 4900 scale(1)(글이 빠진 틈에 부드럽게 물러남) (linear) → 6000 scale(1.00978). 4900 → 다음 바퀴 3400 까지 같은 빠르기로 다가간다.
- 빛 웅덩이 (이 장의 큰 움직임 — 조명이 켜지며 사물 위로 빛이 떨어진다): 0 opacity 1 scale 1 (ease-sine) → 2000 opacity .8 scale 1.03 (ease-sine) → 3400 opacity 1 scale 1 (ease-in) → 3850 opacity .35 scale .9 → 4300 까지 유지 (ease-inout) → 5200 opacity 1 scale 1 → 6000.
- 빛 한 번 스치기 (유지 구간이 멈춰 있지 않게): 위 CSS 의 .sweep(.shade 바로 앞). transform = translateX(x) rotate(18deg). 0 x −760px(왼쪽 밖) → 700 유지 (ease-inout) → 2500 x 1480px(오른쪽 밖) → 같은 시각 −760px 로 순간 이동(양쪽 다 판면 밖이라 안 보인다) → 6000 −760px. 글자·카드가 다 선 뒤 벽을 따라 한 번 지나간다. (−520 / 1240 이면 띠 끝이 모서리에 걸려 보이고 이음매에서 튄다.)
- 천장 조명 (glow breathe): 0 opacity 1 scale 1 (ease-sine) → 1900 opacity .85 scale 1.04 (ease-sine) → 3400 opacity 1 scale 1 (ease-in) → 3850 opacity .55 scale .94 → 4300 까지 유지 (ease-out) → 5400 opacity 1 scale 1 → 6000.
5장(결말 — 유지 중 그릇 위 조명이 한 번 더 밝아진다): 0 opacity .85 scale 1 → 600 유지 (ease-sine) → 1400 opacity 1 scale 1.06 (ease-sine) → 3400 opacity .95 scale 1.02 (ease-in) → 3850 opacity .55 scale .94 → 4300 유지 (ease-out) → 5400 opacity .85 scale 1 → 6000.
- 벽 제목 (비닐 글자 한 자씩): i번째 글자(띄어쓰기 포함, 0부터): 0 보임(opacity 1, translateY(0)) → 3400 유지 (ease-in) → 3850 opacity 0·translateY(-10px) → 같은 시각 translateY(14px) → IN+i×35 까지 유지 (ease-expo) → +700 에 보임 → 6000.
- 캡션 카드 (벽에 걸기): 0 보임 → 3400 유지 (ease-in) → 3850 opacity 0·translateY(-10px) → 같은 시각 translateY(28px) rotate(-2.2deg) → 4900 까지 유지 (ease-expo) → 5500 opacity 1·translateY(0) rotate(0) → 6000.
- 카드 띠 (line draw, 위쪽 기준): 0 scaleY(1) → 3850 유지 → 같은 시각 scaleY(0)(카드가 투명한 동안) → 4950 까지 유지 (ease-inout) → 5350 scaleY(1) → 6000. 세로선이 먼저 그어진 뒤 글줄이 올라온다.
- 카드 글줄 (line mask reveal): i번째 줄(카드 안 문서 순서, 전시 번호 줄 포함) s = i × 80: 0 translateY(0) → 3850 유지(퇴장은 카드 opacity 로 함께 빠진다) → 같은 시각 110% → LINE_IN+s 까지 유지 (ease-expo) → +600 에 0 → 6000.
- 강조 (핵심 낱말 색 채움): 1~4장 카드 첫 칸에서 뜻의 중심 낱말 하나(샘플 1장 '세 가지', 2장 '쌀뜨물', 3장 '부드러운 수세미', 4장 '하루' — 겉 글자도 600 으로 조금 굵게)를 <em class="k">낱말<i class="k1" aria-hidden="true">낱말</i></em> 로 감싼다. .k1 은 같은 글자를 짙은 초록(color-mix(in srgb,var(--c3) 78%,#000))으로 바로 위에 겹친 것. clip-path: 0 inset(0 100% 0 0) → 150 유지 (ease-inout) → 650 inset(0 0 0 0)(왼쪽부터 --c3 로 칠해짐 — 카드 글줄이 다 앉은 뒤) → 3850 유지 → 같은 시각 inset(0 100% 0 0)(카드가 이미 투명) → 6000.
# 넣을 글자
맨 위 「내 캐러셀」의 넣을 글자(1장~5장 줄)를 그대로 쓴다.
(한 줄에 한 장씩, 역할은 / 로 나눈다 — 1장: 제목 / 보조 · 2장: 소제목 / 본문 · 3장: 소제목 / 본문 · 4장: 소제목 / 본문 · 5장: 한 줄)
(한 칸 안에서 줄을 바꾸려면 // 를 쓴다. // 는 줄바꿈 표시일 뿐 화면에 쓰지 않는다. // 가 없는 제목은 뜻이 끊기지 않는 곳에서 알아서 끊는다.)
(채운 예: 3장: 식기세척기는 피해요 / 센 물살에 유약이 상해요 // 부드러운 수세미로 닦아요)
# 글자 규칙
- 한글은 주어진 글자를 한 글자도 바꾸지 말고, 띄어쓰기까지 그대로 넣는다. 로마자로 바꾸지 않는다.
- 줄바꿈은 주어진 대로 지킨다(// 자리에서 끊는다). 제목이 2줄이면 어디서 끊을지 내가 정하지 않았을 때는 뜻이 끊기지 않는 곳에서 끊는다.
- 한 줄 = 하나의 마스크 틀(<span class="ln"><span>글자</span></span>). 줄마다 따로 움직이게 한다.
- 링크 주소·@아이디·해시태그·가격·날짜는 넣지 않는다.
# 지킬 것
- 받침대 위 사물은 하나뿐이다(사진). 코드로 그림·액자·조명 기구를 더 그리지 않는다.
- 캡션 카드 글자가 카드 밖으로 넘치지 않는다. 카드에는 넣을 글자와 전시 번호 말고 가짜 설명문을 채우지 않는다.
- 사람은 나오지 않는다. 실제 미술관·작가 이름을 쓰지 않는다.
- 사진은 연출한 이미지다. 실제 제품 사진·후기처럼 쓰지 않는다.
- 움직임은 위에 적은 것만. 흔들림·튀는 글자·반짝이 효과를 더하지 않는다. 사진을 늘리거나 찌그러뜨리지 않는다.
# 에셋 — 이미지 생성이 안 되면 이 주소를 그대로 쓴다
아래는 Duchamp 가 미리 만들어 올려 둔 예시 그림이다(글자 없음, 웹에서 바로 불러올 수 있음).
이미지 생성 기능이 없거나 예시와 같은 그림을 원하면 "이미지 먼저 만들기"를 건너뛰고, 코드의 이미지 경로를 아래 주소로 그대로 바꿔 쓴다.
- `assets/s1.jpg` → https://pub-a4aa81cc9139413c99493d10e0d4398d.r2.dev/motion/published/museum-label/60e0dbc4ee1b15cab4289622a9cc10d8bcb995ca3bde5955379f739f056ef8bc.webp
- `assets/s2.jpg` → https://pub-a4aa81cc9139413c99493d10e0d4398d.r2.dev/motion/published/museum-label/72ec3525e5bea72b671e82463c8b2c9fb8a1c7d3a5dbc446496ee7fc4fce830e.webp
- `assets/s3.jpg` → https://pub-a4aa81cc9139413c99493d10e0d4398d.r2.dev/motion/published/museum-label/3d795f02587822595eefac452cddad2d68b274d8895b3d4cfb7d7369431f406b.webp
- `assets/s4.jpg` → https://pub-a4aa81cc9139413c99493d10e0d4398d.r2.dev/motion/published/museum-label/cbd7de4ade9dd2fcac669ebff7cd89e2eb75b78dcd4e2165d95889822d661c73.webp
- `assets/s5.jpg` → https://pub-a4aa81cc9139413c99493d10e0d4398d.r2.dev/motion/published/museum-label/03b2d54c9b2bb04f2214c8efca4048da22170af41abf385a0ff49c80f4f42aaa.webp
내 사진·그림으로 바꾸려면: 같은 역할의 이미지를 만들어(누끼는 배경이 투명한 PNG) 위 주소 대신 그 파일 경로를 쓴다. 크기·위치 규칙은 위 명세 그대로 둔다.
# 출력 형식 (반드시)
- 완성된 museum-label.html 하나를 코드 블록 하나로 준다. 설명은 코드 뒤에 짧게.
- body 는 `<body data-dur="6000" data-phase="4075">` 로 연다. 스크립트는 한 장 길이 D 를 document.body.dataset.dur 에서, 재생 시작점을 dataset.phase 에서 읽는다.
- 외부 자바스크립트 라이브러리를 쓰지 않는다. 움직임은 CSS @keyframes 또는 Web Animations API(element.animate)만 쓴다.
- 글꼴은 Pretendard Variable(jsdelivr CDN: https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css) 만 불러온다. 글자는 모두 실제 텍스트다(이미지로 굽지 않는다).
- :root 에 색 변수 세 개를 둔다: --c1 = 《바탕색》, --c2 = 《글자색》, --c3 = 《포인트색》. 모든 색은 이 세 변수와 흰색·검정의 섞음(color-mix)으로만 만든다.
- 사진은 museum-label.html 옆 assets/ 의 s1.jpg ~ s5.jpg 를 상대 경로로, <img> 가 아니라 .room 의 style="background-image:url(assets/sN.jpg)" 로 넣는다.
- 각 장은 <section class="stage" data-n="1"> ~ data-n="5"> 이고, 크기는 정확히 가로 1080px · 세로 1350px 이다. 아래 모든 수치는 이 크기 기준 px 이다.
- 주소에 ?slide=N 이 없으면 다섯 장을 세로로 쌓아 절반 크기(zoom .5)로 보여 준다.
- 주소에 ?slide=N 이 있으면 N번째 장 하나만 1080×1350 그대로, body 여백 0 으로 보여 준다.
- 주소에 ?slide=N&fit=1 이 있으면 그 장 하나를 창 크기에 맞춰 줄이고(zoom = min(창 너비/1080, 창 높이/1350)) 검은 바탕 가운데에 둔다. 화면 녹화용이다.
- 녹화용 함수 두 개를 전역에 둔다 (스크립트 맨 앞, 애니메이션을 만들기 전에 정의):
window.__duration = 한 장 길이(ms);
window.__seek = ms => document.getAnimations().forEach(a => { a.pause(); a.currentTime = ms; });
# 반복 규칙 (seamless loop, 반드시)
- 한 장 안의 모든 애니메이션은 길이가 6000ms 로 같고 iterations: Infinity(무한 반복)이다. fill: 'both'.
- 모든 애니메이션은 0ms 와 6000ms 의 상태가 똑같다. 그래서 마지막 프레임 다음에 첫 프레임이 와도 튀지 않는다.
- 아래 박자 수치는 "완성 화면 = 0ms" 기준으로 적었다. 보는 순서는 **등장 → 유지 → 퇴장**이어야 하므로 재생 시작점을 글자가 비어 있는 틈(3850~4300)의 한가운데 4075ms 로 옮긴다: 모든 애니메이션을 만든 뒤 `document.getAnimations().forEach(a => { a.currentTime = 4075; a.play(); })`, `window.__seek = ms => document.getAnimations().forEach(a => { a.pause(); a.currentTime = ms + 4075; })`. 그러면 0초에 글자가 들어오기 시작하고, 끝에서 빠진 뒤 처음으로 이어진다.
- 시차(stagger)는 animation-delay 로 주지 않는다. delay 는 첫 바퀴에만 먹어서 둘째 바퀴부터 어긋난다. 대신 키프레임 시각을 줄마다 밀어서 만든다. 아래처럼 ms 로 키프레임을 적는 작은 도우미를 쓴다:
function loop(el, keys) { // keys = [[ms, {css}, 다음 칸까지의 이징], ...]
return el.animate(keys.map(([ms, css, ease]) => ({offset: ms / D, easing: ease || 'linear', ...css})), {duration: D, iterations: Infinity, fill: 'both'});
}
- 같은 시각에 키프레임 두 개를 두면 순간 이동이다(가려진 동안 아래로 되돌릴 때 쓴다). 키프레임 시각은 항상 앞에서 뒤로 커지거나 같아야 한다.
# 이징 토큰 (이 값 그대로)
--ease-out: cubic-bezier(.22,.61,.36,1) · --ease-expo: cubic-bezier(.16,1,.3,1) · --ease-in: cubic-bezier(.55,0,.8,.2) · --ease-inout: cubic-bezier(.65,0,.35,1) · --ease-sine: cubic-bezier(.37,0,.63,1)
# 영상으로 렌더 (코드를 만든 다음 네가 직접)
- 이 단계는 Claude Code·Codex 처럼 이 컴퓨터에서 명령을 실행할 수 있을 때 한다. 명령을 실행할 수 없는 채팅 앱이면 HTML 파일만 주고, "Claude Code나 Codex에 이 프롬프트를 붙여 넣으면 영상 파일까지 만들어진다"고 알려 줘.
1. 작업 폴더 duchamp-museum-label/ 에 museum-label.html 을 저장한다. 그림 파일을 쓰면 같은 폴더의 assets/ 에 둔다(에셋 주소를 그대로 쓴 경우는 필요 없다).
2. Node 와 ffmpeg 가 있는지 확인한다. 없으면 설치 명령을 보여 주고, 설치해도 되는지 나에게 먼저 묻는다.
3. 헤드리스 브라우저(Playwright 또는 Puppeteer)로 ?slide=N 주소를 창 크기 1080×1350, 배율 1 로 연다. document.fonts.ready 와 모든 그림 로드가 끝날 때까지 기다린다.
4. 실시간으로 녹화하지 않는다. 한 바퀴 길이 D = window.__duration 동안 1/30초마다 window.__seek(ms) 로 멈추고 두 번 requestAnimationFrame 을 기다린 뒤 PNG 로 캡처한다(D=5000 이면 150장).
- 시작점 S 는 0 이 아니라 그 장의 글자·그림이 모두 나타나 멈춰 있는 시점으로 잡는다(네가 짠 키프레임에서 고른다). 인스타그램은 첫 프레임을 표지로 보여 주는데, 0ms 는 화면이 비어 있다. S 부터 S+D 직전까지 찍고, D 를 넘는 시각은 D 로 나눈 나머지로 seek 한다 — 한 바퀴가 이어지게 만들어져 있어 어디서 시작해도 이음매가 없다.
5. ffmpeg 로 묶는다: ffmpeg -y -framerate 30 -i frames/%04d.png -c:v libx264 -pix_fmt yuv420p -crf 18 -movflags +faststart out/slide-N.mp4
6. N = 1~5 반복한 뒤 프레임 PNG 는 지운다. 각 영상이 1080×1350 이고 길이가 D 와 같은지 ffprobe 로 확인한다.
7. 끝나면 out/slide-1.mp4 ~ slide-5.mp4 경로를 알려 주고, 인스타그램 게시물 → 여러 장 선택으로 1~5 순서대로 올리면 된다고 안내해 줘. 한 바퀴의 끝과 처음이 이어지게 만들어져 있어 반복 재생이 자연스럽다.
만드는 법
- 고르고 복사
영상이나 이미지를 고르고 프롬프트를 복사하세요. 맨 위 「내 캐러셀」 칸에 브랜드·색·주제·1장~5장 글자를 채우세요.
- 붙여 넣기
영상은 Claude Code·Codex에, 이미지는 ChatGPT에 붙여 넣으세요. 이미지는 장마다 모든 글자의 받침·모음을 확인하고, 틀리면 그 장만 다시 생성하세요.
- 올리기
영상은 장마다 MP4 파일로, 이미지는 그림 파일로 받아요. 인스타그램에서 여러 장 선택으로 순서대로 올리세요.
글자 분량
- 1장 · 표지
- 제목 16자 · 보조 24자
- 2~4장 · 본문
- 소제목 16자 · 본문 60자
- 5장 · 마지막 장
- 한 줄 16자
한도를 넘기면 ChatGPT가 그리지 않고 줄여 달라고 되묻습니다.
지킬 것
- 잘 맞는 글: 공예·제품 하나 소개, 만드는 과정, 관리법
- 사물 장면은 [주제] 칸에 한 줄로 적어 주세요. 내 제품 사진처럼 보이지만 연출된 이미지입니다.
- 캡션 카드는 작으니 본문은 짧게 쓰세요.
- 모든 글자의 받침·모음을 한 번 확인하세요(예: 밤/밥, 붙/불, 꽃/꽂). 틀리면 그 장만 다시 생성하세요.