촛불 지키기
바람 드는 긴 복도를 촛불 들고 걷는다 — 바람이 오면 손으로 가려 불꽃을 지킨다.
프롬프트를 복사해 claude.ai 에 붙여 넣으면 바로 실행됩니다 · Opus 5.5 · Effort High 기준
- 권장 모델
- Claude Opus 5.5 · Effort High
- 화면비
- 16:9 (가로)
- 조작
- 누르고 있으면 손으로 가리기(대신 느려짐) · 떼면 걷기 · 키보드는 아무 키
- 결과물
- HTML 파일 하나 · 따로 설치할 것 없음
프롬프트
Claude 가 읽는 제작 지시문입니다. 맨 위 [바꿀 곳]의 값을 바꿔 붙여 넣어도 됩니다.
밤의 긴 마루 복도를 촛불 하나 들고 걸어가는 브라우저 미니게임 「촛불 지키기」를 HTML 파일 하나로 만들어 줘.
핵심 아이디어: 화면 아래 가운데에 내가 든 촛불이 크게 있고, 앞으로는 창호 문이 양쪽에 늘어선 어두운 복도가 소실점까지 이어진다. 떼고 있으면 앞으로 걷고, 누르고 있으면 손바닥으로 불꽃을 가린다(대신 느려진다). 문틈에서 바람이 불면 불꽃이 기울고 가늘어지고, 그 빛을 받는 벽과 창호가 함께 흔들리고 어두워진다. 불꽃의 크기 자체가 남은 목숨이다. 꺼지기 직전 불꽃이 깜빡일 때 복도 전체가 같이 꺼졌다 켜지는 그 순간이 이 게임의 긴장이다.
[바꿀 곳]
- WALK_SPEED = 1.6 (m/초, 떼고 있을 때 걷는 속도)
- SHIELD_SPEED = 0.5 (m/초, 가리고 있을 때 속도)
- SHIELD_BLOCK = 0.88 (가렸을 때 막는 바람 비율)
- FLAME_MAX = 100 (불꽃 기운 최댓값)
- DRAIN = 50 (바람 세기 1 일 때 가리지 않은 불꽃이 1초에 잃는 기운)
- RECOVER = 6 (바람이 없을 때 1초에 되찾는 기운)
- FIRST_GUST = 2.5 (판 시작 뒤 첫 바람이 부는 시각, 초)
- TELL = 0.9 (바람이 불기 전 문이 덜컹이는 예고 시간, 초)
- 색: BG #14110E, WOOD #5A4030, HANJI #D9CBB0, WAX #E8DFCC, FLAME_CORE #FFF4D6, FLAME_BODY #FFB347, FLAME_EDGE #E0672A, FLAME_BASE #4A6FA5, DRAFT #9FB4C0, HAND #2A1C14, HAND_RIM #E89A55
[크기]
- 캔버스는 창 전체. S = min(W, H). 모든 크기는 S 의 배수로 정한다. 기본 모양은 16:9 가로지만 판을 따로 자르지 않고 복도 장면이 창 전체를 채운다(세로 폰에서도 같은 식으로 그려진다).
- 소실점: (0.5W, 0.42H). 원근 초점 거리 f = 0.9S. 세계 좌표의 점 (x 옆, y 높이, z 앞 거리, 단위 m)은 화면 (0.5W + f·x/z, 0.42H + f·(1.5 − y)/z) 에 놓인다(눈높이 1.5m).
- 복도 폭 2.4m(x = −1.2 ~ +1.2), 천장 높이 2.6m. z = 0.35 ~ 26m 사이만 그린다.
- 초: 심지(불꽃 아래 끝)가 (0.5W, H − 0.30S). 초 몸통은 폭 0.07S, 심지에서 화면 아래 끝까지.
- 불꽃: 바람 없고 기운이 가득할 때 높이 0.17S, 폭 0.055S. 가로 화면(H = S)이라면 불꽃 끝이 0.53H 근처 — 화면 높이의 6분의 1을 차지하는 큰 불꽃이다.
- 가리는 손: 높이 0.32S 의 오므린 손바닥 실루엣. 가리지 않을 때는 화면 밖(불꽃에서 옆으로 0.45S), 가릴 때 불꽃 바람 쪽 0.09S 옆까지 0.12초에 미끄러져 들어온다.
- 글자: 점수 0.045S(왼쪽 위, 여백 0.03S), 소리 단추 0.07S 정사각(오른쪽 위), 제목 0.075S, 안내 0.032S.
[장면]
- 한옥 안채의 긴 마루 복도, 밤. 바닥은 결이 앞뒤로 흐르는 마루널(복도 방향 줄 7개 + 0.9m 마다 가로 이음), 천장에는 3m 마다 서까래 보 하나. 양쪽 벽은 3m 단위가 되풀이된다: 기둥 0.2m(WOOD) → 창호 미닫이 1.6m(HANJI 바탕에 가로세로 살, 살 간격 0.2m) → 회벽 1.2m(#8A7F6E). 왼쪽 벽과 오른쪽 벽은 1.5m 어긋나 있다. 창호 높이 2.1m.
- 걸은 거리 p(m)가 늘면 세계 위치 w 의 물체는 z = w − p 에 그려진다. 먼 것부터 가까운 것 순으로 다각형을 채운다.
- 빛: 이 장면의 빛은 오직 촛불 하나다. 불꽃 밝기 L = (현재 불꽃 높이 / 0.17S) × 깜빡임 값(0~1.1). 각 다각형의 색 = 바탕색 × min(1, 0.04 + 1.1·L / (1 + (z / 2.6)²)). 먼 복도는 거의 검다. 그 위에 불꽃 둘레로 반지름 1.1S·L 의 따뜻한 방사 그라데이션(FLAME_BODY, 가운데 알파 0.22)을 'lighter' 로 한 번 얹는데, 그 중심은 불꽃 기울기를 따라 옆으로 0.15S × 기울기/50° 만큼 움직인다 — 이것이 벽에 비친 빛이 흔들리는 효과다. 손으로 가리면 손 쪽 벽의 밝기는 0.5배가 된다(손 그림자).
- 화면 가장자리는 어두운 비네트(가장자리 알파 0.6). 창 밖으로 남는 곳 없이 장면 전체가 어둠으로 이어진다.
- 불꽃: 아래가 둥근 물방울 모양을 베지어 곡선으로 세 겹 그린다 — 바깥 FLAME_EDGE(알파 0.55), 몸통 FLAME_BODY 에서 FLAME_CORE 로 가는 세로 그라데이션, 심지 바로 위 작은 FLAME_BASE 푸른 밑동(높이의 0.15). 바깥에 반지름 2.5×불꽃 높이의 은은한 빛무리 하나('lighter', 알파 0.35).
- 불꽃의 움직임: 바람이 없을 때도 시드 위상의 사인 세 개(1.3·2.9·7.1Hz)를 더해 끝이 좌우로 ±0.006S, 높이가 ±4% 흔들린다. 바람을 맞으면 끝이 바람 반대쪽으로 기운다(왼쪽 문 바람이면 방향 +1, 오른쪽으로 기움): 기울기 = 방향 × min(1, 세기) × (1 − 0.85·가림) × 50°. 첫 바람(세기 0.66)을 가리지 않으면 약 33°. 노출 e = 세기 × (1 − 가림) 일 때 높이 × (1 − 0.45·min(e,1)), 폭 × (1 − 0.3·min(e,1)), 끝이 12Hz 로 ±0.012S 떨린다. 기운에 따라 높이 × (0.3 + 0.7·기운/100).
- 꺼지기 직전: 기운이 25 아래면 0.08초마다 확률 (25 − 기운)/25 × 0.5 로 깜빡임이 일어나 0.06~0.14초 동안 L 이 0.2배가 된다. 그 순간 벽·창호·바닥이 다 같이 꺼질 듯 어두워진다. 이것이 시그니처 순간이다.
- 바람: 바람을 낼 문은 왼쪽 또는 오른쪽(시드 난수) 벽에서 예고가 시작될 때 z ≥ 2.0 인 가장 가까운 창호로 정하고, 그 바람이 끝날 때까지 같은 문(세계 위치 고정)을 쓴다. 가리는 손은 바람 쪽에서 들어온다. 예고(TELL) 동안 그 창호가 ±0.004S 로 덜컹이고 한지에 DRAFT 색이 알파 0.15 로 비치며, 문이 0.08m 열려 검은 틈이 생긴다. 불꽃은 예고 동안 이미 10% 만큼 기운다. 바람이 부는 동안 그 문틈에서 불꽃 쪽으로 가는 바람 줄기 18개(DRAFT, 알파 0.25, 길이 0.08~0.2S, 굵기 0.003S)가 흐른다.
- 꺼질 때: 불꽃이 0.15초에 줄어들어 사라지고, 심지에서 회색 연기 한 줄기가 1.2초 동안 휘며 올라간다. 그동안 장면은 완전히 어둡고(L = 0) 심지 끝만 붉게 남는다.
[조작]
- 키보드: 아무 키(스페이스·화살표·Enter 등, M 제외)를 누르고 있으면 손으로 가리기, 떼면 걷기. 판을 시작하는 첫 키도 계속 누르고 있으면 그대로 가리기가 된다.
- 터치·마우스: 화면 아무 곳이나 누르고 있으면(끌어도 마찬가지) 가리기, 떼면 걷기. 폰 세로 화면에서 엄지 하나로 된다.
- 눌림 상태는 키 하나라도 눌려 있거나 포인터가 하나라도 닿아 있으면 참. 가림 값 shield 는 누르면 0.12초에 0→1, 떼면 0.12초에 1→0 으로 바뀐다. 눌렀다 같은 프레임에 뗀 입력도 keydown 때 shield 를 최소 0.15초 유지하도록 기록해 둔다.
- 손은 입력한 프레임에 바로 움직이기 시작한다.
[규칙]
- 속도 = WALK_SPEED 와 SHIELD_SPEED 사이를 shield 로 보간. 점수 = 걸은 거리(m)의 정수부. 화면 왼쪽 위에 「12 m」 처럼 보인다.
- 바람 일정(판 시작부터 시각 t 초): 첫 바람 시작 FIRST_GUST. 세기 s(t) = min(1.8, 0.6 + 0.025t). 다음 바람까지 간격 I(t) = max(2.2, 4.2 − 0.05t). 지속 D(t) = min(1.2 + 0.015t, I(t) − 0.6). 예고는 시작 TELL 초 전부터.
- 기운: 바람이 부는 동안 1초에 DRAIN × s × (1 − SHIELD_BLOCK × shield) 를 잃는다. 바람이 없으면 1초에 RECOVER 씩 FLAME_MAX 까지 되찾는다. 기운이 0 이 되면 꺼진다 — 판 끝.
- 검산(기본값): 아무 입력이 없으면 첫 바람에 41 을 잃고(100→59), 둘째 바람(6.6초) 뒤 약 26, 셋째 바람(10.4초) 도중 약 11.4초에 꺼진다 — 40초 안에 반드시 끝난다. 바람마다 제때 가리면 처음 20초 동안 기운은 85 아래로 내려가지 않는다(바람 한 번에 5~12 잃고 다음 바람 전에 거의 되찾는다). 20초 뒤부터는 가려도 밀려서 30초에 약 75, 35초에 약 50, 그 뒤 1초에 약 6~7 씩 줄어 완벽한 플레이도 약 40~45초에 꺼진다. 기본 설정에서 처음 하는 사람의 판은 15~40초, 점수 10~50m.
- 실패 이유가 보이게: 'over' 화면에 「왼쪽 문 바람에 꺼졌다」/「오른쪽 문 바람에 꺼졌다」, 점수, 이번 판 최고 점수(메모리), 「눌러서 다시」.
- 꺼지는 연출(1.35초) 동안 state 는 'play', 끝나면 'over'. 'over' 는 뜨자마자 키·탭을 받아 새 판을 연다.
[구경 모드]
- 열리자마자 state 'demo'. 같은 게임 규칙과 장면을 컴퓨터가 손을 움직여 플레이한다. 점수·최고 점수는 바꾸지 않는다.
- 위 가운데(0.16H)에 제목 「촛불 지키기」, 그 아래(0.24H)에 「눌러서 시작 — 누르고 있으면 가리기, 떼면 걷기」. 글자 색 HANJI, 알파 0.85.
- 컴퓨터의 손: 바람이 시작된 뒤 0.15~0.35초(시드 난수) 뒤 가리고, 바람이 끝나고 0.15~0.3초 뒤 뗀다. 다만 정해진 연출이 있다(판 시작부터 잰 시각):
- 첫 바람(2.5초)은 0.7초 늦게 가린다 → 2.5~3.2초에 불꽃이 약 33° 기울고 가늘어지며 벽 빛이 한쪽으로 쏠린다. 기운 100→약 75.
- 둘째 바람(6.6초)은 1.0초 늦게 가린다 → 약 52.
- 셋째 바람(10.4초)은 가리지 않는다 → 약 11.4초에 기운이 25 아래로 떨어져 꺼지기 직전 깜빡임이 시작되고, 바람 끝(11.8초)에 약 9.
- 넷째~여섯째 바람은 시작 0.05초 뒤 바로 가린다 → 기운이 약 13~25 사이를 오가며 살아남고, 깜빡임이 약 11.4초부터 20초 무렵까지 이어진다.
- 그 뒤로는 바람 네 번에 한 번꼴로 0.5초 늦는다.
- 구경 모드의 불이 꺼지거나 45초가 지나면 시드를 하나 늘려 구경 판을 새로 시작한다.
- 첫 화면부터 큰 불꽃·불빛 받은 창호·소실점까지 이어진 복도가 보인다. 빈 화면으로 시작하지 않는다.
[소리]
- WebAudio 합성음만, 첫 입력 뒤에 AudioContext 를 만든다. 주 음량 0.5.
- 바람: 시드 난수로 채운 2초 잡음 버퍼를 반복 재생 → 대역 통과(중심 600Hz, Q 0.8) → 음량은 현재 바람 세기 × (1 − 0.6·shield) × 0.4 를 0.1초에 걸쳐 따라간다.
- 예고 덜컹: 톱니파 90→60Hz, 저역 통과 500Hz, 0.25초.
- 깜빡임 때 작은 탁 소리(잡음 0.03초, 고역 통과 2000Hz, 음량 0.15).
- 꺼짐: 잡음 0.35초, 저역 통과 800→200Hz 로 내려가는 「후」 소리.
- 판 시작: 사인파 660Hz 0.4초, 음량 0.15, 서서히 줄임.
- 오른쪽 위에 소리 끄기 단추(스피커 모양 선 그림), M 키로도 끄고 켠다. 단추를 누르거나 M 을 눌러도 게임은 시작되지 않는다.
[구현]
- HTML 파일 하나로 만든다. 바깥 파일·글꼴·라이브러리·네트워크 요청을 쓰지 않는다(미리보기 칸에서 막힌다).
- 첫 <script> 맨 앞에 CONFIG 객체를 두고 [바꿀 곳]의 값과 이 글에 나온 숫자(속도·크기·색·시간)를 이름 붙여 모은다.
- 게임 진행은 requestAnimationFrame 의 타임스탬프로 고정 시간 간격(1/120초) 갱신을 돌린다. 한 프레임에 밀린 시간은 0.1초까지만 따라잡는다. setTimeout·setInterval·CSS 애니메이션으로 움직이지 않는다.
- 난수는 시드 고정 난수(mulberry32 등)만 쓰고, 판마다 시드를 하나씩 늘린다. Math.random 은 쓰지 않는다.
- 캔버스는 늘 창 전체 크기다. 창 크기가 바뀌면 다시 맞추고, 게임판 비율이 정해진 게임은 판을 가운데 두되 남는 곳에도 배경을 그린다(빈 띠를 두지 않는다). 스크롤이 생기지 않게 한다. devicePixelRatio 는 2 까지만 선명도에 반영한다.
- 키보드와 터치를 둘 다 받는다. 키 입력은 window 의 keydown·keyup 으로 받아, 화면을 먼저 누르지 않아도 첫 키가 먹게 한다. 짧게 눌렀다 뗀 키도 놓치지 않게 keydown 때 처리하거나 기록해 둔다. 터치는 페이지 스크롤·확대가 일어나지 않게 막는다(touch-action: none).
- 점수·상태를 window.__game = { state, score } 에 두고 매 프레임 갱신한다. state 는 'demo'(구경 모드)·'play'·'over' 셋 중 하나, score 는 숫자다.
- 열리자마자 'demo' — 컴퓨터가 스스로 플레이하는 구경 모드와 「눌러서 시작」 안내. 첫 키 입력이나 탭이면 새 판 'play'. 판이 끝나면 'over' 화면, 거기서 키 입력이나 탭이면 새 판. 'over' 화면은 뜨자마자 키 입력·탭을 받는다(입력을 막는 대기 시간을 두지 않는다). 끝나는 연출이 있으면 그동안은 state 를 'play' 로 두고, 연출이 끝나 입력을 받을 수 있게 된 때 'over' 로 바꾼다.
- 플레이 중 아무 입력이 없으면 늦어도 40초 안에 판이 끝난다.
- 브라우저 저장소(localStorage·sessionStorage·IndexedDB·쿠키)를 쓰지 않는다 — 미리보기 칸에서 막혀 오류가 난다. 최고 점수는 메모리에만 둔다.
- 소리는 WebAudio 로 합성한 소리만, 첫 입력 뒤에만 낸다. 화면 구석에 소리 끄기 단추(M 키도)를 둔다.
- 탭이 숨으면 멈추고 돌아오면 이어서 한다.
- 글자는 시스템 글꼴만 쓴다: 'Pretendard', 'Apple SD Gothic Neo', 'Malgun Gothic', 'Noto Sans KR', sans-serif.
- 모든 함수·상수를 먼저 정의하고, 시작(초기화와 첫 requestAnimationFrame)은 스크립트 맨 끝에서 한 번만 부른다.
- 콘솔에 오류가 나지 않게 한다. 스스로 대조하는 표는 console.table, 어긋남은 console.warn 으로만 낸다.
이 작품만의 구현:
- 상태 갱신은 함수 step(sim, dt, input) 하나로 모은다(sim = { t, p, life, shield, gust 목록, rng }). 실제 판·구경 모드·시작 때의 검산이 모두 같은 함수를 쓴다.
- 바람 일정은 판 시작 때 시드 난수로 쪽(왼/오)만 정하고, 시각·세기·지속은 [규칙]의 공식 그대로 미리 만들어 둔다.
- 시작할 때 화면 없이 step 을 돌려 console.table 로 대조한다: ① 입력 없음 → 꺼지는 시각(기대 9~16초), ② 바람 시작에 맞춰 가리고 끝나면 떼는 손 → 꺼지는 시각(기대 35~60초)과 처음 20초 최저 기운(기대 ≥ 85), ③ [구경 모드]의 연출대로 움직이는 손 → 기운이 처음 25 아래로 내려가는 시각(기대 10.5~12.5초)과 20초까지 꺼지지 않음, ④ 모든 바람에서 D(t) ≤ I(t) − 0.6. 벗어나면 console.warn.
- 그리기 순서: 바탕 채움 → 천장·벽·창호·바닥(먼 것부터, 원근 사다리꼴) → 바람 줄기 → 빛 그라데이션 → 초 몸통 → 불꽃 → 손 → 비네트 → 글자·단추. 창호 살은 z < 8 인 창호만 선으로 그리고, 그보다 먼 창호는 면 하나로 칠한다.
- 깜빡임 값은 갱신 단계에서 정하고(시드 난수), 그리기는 그 값만 읽는다.
- 손은 베지어 곡선 실루엣(HAND)에 불꽃 쪽 가장자리만 HAND_RIM 테두리(굵기 0.004S, 알파 0.6 × L). 손가락 네 개가 오므린 윤곽이 보이는 정도로, 얼굴·팔·사람 몸은 그리지 않는다.
- 결과 파일은 100KB 이하.
[하지 말 것]
- 불티·반딧불·먼지 같은 장식 입자를 띄우지 않는다 — 촛불 하나의 빛과 어둠만이 이 작품의 그림이다. 움직이는 선은 바람 줄기 18개와 꺼질 때 연기 한 줄기뿐.
- 렌즈 플레어·화면 전체 블룸·발광 겹치기를 하지 않는다 — 빛무리는 불꽃 둘레 하나, 벽 빛 그라데이션 하나만. 더 얹으면 어둠이 사라져 깜빡임의 긴장이 죽는다.
- 생명 막대·숫자 게이지를 따로 그리지 않는다 — 불꽃 크기가 곧 목숨이다.
- 화면 모서리에 「WIND」 같은 라벨·화살표 경고를 붙이지 않는다 — 바람 예고는 덜컹이는 문과 기우는 불꽃으로만 읽힌다.
- 사람 몸·얼굴·만화 캐릭터를 그리지 않는다 — 보이는 것은 초와 손뿐인 일인칭이다.
- 바탕을 남청색이나 고채도 색으로 칠하지 않는다 — 어둠은 따뜻한 갈흑색 BG 다.
- 바깥 글꼴·이미지·라이브러리를 쓰지 않는다 — 미리보기 칸에서 막힌다.
- Math.random·저장소·setTimeout 을 쓰지 않는다 — 판이 되풀이되지 않거나 오류가 난다.
사용 방법
필요한 것: claude.ai 계정 — 무료 계정에서도 Claude Opus 5.5 를 고를 수 있습니다
- claude.ai 에서 새 대화를 엽니다.
- 모델 메뉴에서 Claude Opus 5.5 를 고르고, 같은 메뉴의 Effort(답변 깊이)를 High 로 둡니다.
- 복사한 프롬프트를 붙여 넣고 전송합니다. 결과로 나온 HTML 파일을 브라우저로 열면 바로 해 볼 수 있습니다.
추가 요청 예시
결과가 나온 뒤 같은 대화에서 이어서 요청하면 해당 부분만 수정됩니다.
- 복도 끝에 닿으면 문이 열리고 다음 복도로 넘어가게 해 줘
- 바람이 오기 전에 문이 덜컹이는 소리를 조금 더 크게 내 줘
- 불꽃을 푸른빛이 도는 촛불로 바꿔 줘
미니게임