Build "Harbor Jelly" — a single self-contained HTML file (no external requests, no libraries) that renders an interactive 3D diorama with WebGPU/WGSL: a small working container harbor set inside a square block of wobbly, translucent jelly sea, sitting on a paper studio backdrop. It belongs to a series called "Material Studies", so match that look: warm paper background, serif italic display title ("Harbor Jelly."), small uppercase eyebrow "MATERIAL STUDIES", a status pill top-right ("WebGPU · Live"), a frosted control panel on the right, and a "How to play" line bottom-left. English UI. Light theme only, by design.
THE BLOCK
- A 5.2 × 5.2 block seen from a 3/4 orbit camera. The ground and sea are cut flat at the block's sides, so you see candy-coloured soil strata in cross-section, and the sea's cut faces show everything underwater through the jelly.
- The water is a real-time linear shallow-water simulation on a staggered grid (heights at cell centres, velocities on faces) with a little surface tension, so it feels like jelly. Render it with refraction, absorption by depth, caustics projected onto the floor, light shafts, a Fresnel studio reflection, foam (wakes, splashes, a shoreline lace) and a bright meniscus on the cut edges.
- The whole block can be tipped by dragging its side. The water sloshes to the low side, the jelly shears and quivers, then it settles flat again.
THE HARBOR (layout)
- Along the back runs a vertical concrete quay wall, with rubber fenders, bollards, ladders, and mussels and weed crusting it below the waterline.
- Left part: a container terminal. A flat asphalt yard with painted lines, concrete strips with two crane rails, light towers, and a small terminal office. Hundreds of colourful containers are stacked in neat rows. Each is a ribbed steel box with door ends and darker corner posts, drawn as GPU instances in about ten liveries.
- Right part: an old stone fishing quay with a cobbled promenade, a corrugated fish-market shed and lamp posts. A timber pier stands on mussel-covered piles with cross-bracing, with two small fishing boats moored alongside.
- Behind it all, a town climbs a grassy hill: three rows of gabled houses in Nordic harbour colours (falu red, ochre, white, pale blue, mint, pink) with tiled or slate roofs, white-framed windows, doors and chimneys. There is a white church with a green copper spire, and some round trees.
- A rubble breakwater shelters the fishing harbour, with a concrete crown wall. Big rocks sit near the crest, smaller weedy ones tumble down under water. On its head stands a red-and-white banded lighthouse with a gallery and a glass lantern, whose lamp slowly turns.
- In the open basin: two small yachts swinging on their moorings (white hulls, coloured stripe, fin keels visible through the jelly, furled sails), yellow mooring buoys, and a green channel buoy, all bobbing on the waves.
- On the floor: pale sand with drifts of silt, weed tufts, stones, starfish, an old anchor with chain, a lost tyre.
THE SHIP'S DAY (fully automatic loop, about 100 s)
1. A small feeder container ship (navy topsides, red antifouling, white accommodation block at the stern with bridge wings, funnel with a yellow band, turning radar, visible propeller) lies alongside with containers on deck, 6 bays × 4 rows × 2 tiers.
2. Two ship-to-shore gantry cranes on the rails, one red and one blue, each with portal legs, a backreach with a machinery house, an A-frame with forestays, a hinged boom, a trolley with a hanging cab, and a yellow spreader on four wire falls. They lower their booms and swap boxes: unload from the tops of the ship's stacks into yard stacks, then load other boxes aboard. Every move is a smooth sequence: travel, trolley out, lower onto the box (tracking the ship's bobbing), latch, hoist, trolley across, lower, unlatch. Cranes never get closer than their leg width; an idle crane moves out of the other's way.
3. When the work is done the trolleys park, the booms swing up, and a tug comes round to the bow. The tug has a black hull, white and red superstructure, a tyre fender all round, a big bow fender, and puffs of funnel smoke. It passes a tow line and backs away, pulling the bow off the quay while the ship swings out. Then the line is cast off, the tug pivots on the spot and goes home, and the ship sounds her horn.
4. The ship sails a lap of the harbour along a smooth path, past the lighthouse. She rides the water on several sample points (heave, pitch, roll) and hands the displaced water back to the sim, so she leaves a real bow wave, wake and white water.
5. Meanwhile the tug goes out to a standby spot. When the ship stops off the quay, the tug noses in and pushes her sideways onto the fenders, with prop wash churning. Then it backs off, the booms come down, and the cranes start again.
- Keep everything collision-free (ship and tug vs. quay, breakwater, pier, yachts and buoy; no grounding). Check it with a headless node test.
LIFE
- Fish schools (mackerel, herring, small orange wrasse) as boids that stay in the water and under the hulls, scatter from fingers, splashes and moving ships, and come to food.
- Moon jellyfish: glassy pulsing bells with four pink rings, frilly oral arms and a fringe of tentacles. They drift with the water flow.
- Herring gulls (white body, grey mantle, black wingtips, yellow bill with a red spot). They circle, bank and glide, and land on perches: lamps, roof ridges, the church spire, the lighthouse gallery, the crane apexes, the ship's mast, the tug, the yachts' masthead. Perches on moving things move with them. They all take off when the horn sounds, and some dive at food.
- Steam puffs from the ship's funnel on the horn; tiny bubbles caught in the jelly.
CONTROLS (right panel; bottom sheet on phones)
- "Cast off" sends the ship early (cranes finish the box in hand). At sea the button becomes "Call her in" and hurries her home; it greys out while busy.
- "Horn" (also Space) plays a synthesized WebAudio ship horn: two low detuned sawtooth reeds a fifth apart, low-passed, starting only after a user gesture. It also blows steam and scatters the gulls.
- "Feed the fish" scatters pellets off the end of the pier; fish rush in and gulls swoop down.
- Tally: Boxes moved / Voyages / Fed. A ship status line ("Unloading 3 / 8", "Loading", "Booms up", "Tug coming", "Casting off", "At sea 40%", "Coming in", "Berthing"). A small activity meter.
- Flavour swatches change the water colour and strata: Sea glass (teal-mint), Blueberry (deep blue), Lime.
- Sliders: Firmness, Wave damping, Tide (±10 cm; the sea level, ships and floating things follow).
- Buttons: Reset, Pause, Sound on/off, Reset view.
POINTER INTERACTION
- Drag across the water to make waves (with a crosshair cursor).
- Drag the side of the block to tip it.
- Pick up an orange-and-white lifebuoy and throw it. Lifebuoys float flat on the waves, bounce off the pier, buildings and breakwater, and are shoved aside by the ship and tug.
- Drag empty space or right-drag to orbit; scroll or pinch to zoom.
- On phones: touch orbit, pinch zoom, and a collapsible bottom sheet for the controls.
RENDERING DETAILS
- Pipeline: shadow map → caustics pass → opaque scene (with a distance buffer) → back-face pass of the water → water pass over a copy of the scene → tone map + dither.
- Moving rigs are static vertex buffers whose vertices carry an object id; about 24 object transforms (position + quaternion) live in the uniform buffer. Cables are drawn with one end in each object's frame, so they stretch: crane falls, forestays, the tow line.
- Material kinds in the prop shader: matte paint, glass, gloss steel, roof tiles, timber grain, corrugated sheet (normal-perturbed ribs), dressed-stone masonry, rubber, rock, glowing lamps, swaying weed, and hulls with antifouling below a boot-top line.
- The jelly shear and squash wobble applies to everything inside the block.
QUALITY BAR
- Smooth on a laptop; simulation under about 2 ms per frame.
- A friendly fallback screen if WebGPU is missing or the adapter fails.
- No console errors, no page scroll. Respect prefers-reduced-motion.
- Ship it with a headless test of the choreography and a Playwright end-to-end test.
한국어 번역
"Harbor Jelly"를 만들어라 — 하나의 자체 포함 HTML 파일(외부 요청 없음, 라이브러리 없음)로, WebGPU/WGSL을 사용해 인터랙티브 3D 디오라마를 렌더링한다: 종이 스튜디오 배경 위에 놓인, 흔들리는 반투명 젤리 바다의 정사각형 블록 안에 들어선 작은 운영 중인 컨테이너 항구. 이 작품은 "Material Studies"라는 시리즈에 속하므로 그 룩을 따라야 한다: 따뜻한 종이 배경, 세리프 이탤릭 디스플레이 제목("Harbor Jelly."), 작은 대문자 아이브로우 "MATERIAL STUDIES", 오른쪽 상단 상태 알약("WebGPU · Live"), 오른쪽의 프로스티드 컨트롤 패널, 왼쪽 하단의 "How to play" 줄. 영어 UI. 의도적으로 라이트 테마만.
블록
- 3/4 궤도 카메라에서 바라본 5.2 × 5.2 블록. 지면과 바다는 블록 측면에서 평평하게 잘려 있어, 캔디 색 토양 지층을 단면으로 볼 수 있고, 바다의 절단면을 통해서는 젤리를 통해 수중의 모든 것이 보인다.
- 물은 엇갈린 격자(셀 중심의 높이, 면의 속도) 위에서 약간의 표면 장력을 지닌 실시간 선형 천수 시뮬레이션이라 젤리처럼 느껴진다. 굴절, 수심에 따른 흡수, 바닥에 투영되는 코스틱, 빛줄기, 프레넬 스튜디오 반사, 거품(항적, 물보라, 해안선 레이스), 절단 모서리의 밝은 메니스커스로 렌더링한다.
- 블록의 측면을 드래그하면 블록 전체를 기울일 수 있다. 물은 낮은 쪽으로 출렁이고, 젤리는 전단되며 떨리고, 이내 다시 평평하게 가라앉는다.
항구 (레이아웃)
- 뒤쪽을 따라 수직 콘크리트 부두 벽이 뻗어 있고, 고무 펜더, 볼라드, 사다리, 그리고 수면 아래에는 홍합과 해초가 달라붙어 있다.
- 왼쪽 부분: 컨테이너 터미널. 페인트 선이 그어진 평평한 아스팔트 야드, 두 개의 크레인 레일이 깔린 콘크리트 스트립, 조명 타워, 작은 터미널 사무실. 수백 개의 형형색색 컨테이너가 가지런한 줄로 쌓여 있다. 각각은 도어가 있는 양 끝과 더 어두운 코너 포스트를 가진 골이 진 강철 박스이며, 약 열 가지 도장을 입혀 GPU 인스턴스로 그려진다.
- 오른쪽 부분: 자갈이 깔린 산책로, 골판 금속 어시장 창고, 가로등이 있는 오래된 돌 어항 부두. 홍합이 덮인 말뚝 위에 가새로 버틴 목재 부두가 서 있고, 옆에 작은 어선 두 척이 정박해 있다.
- 그 배후로 마을이 풀이 무성한 언덕을 타고 올라간다: 기와나 슬레이트 지붕, 흰 테두리 창, 문과 굴뚝을 갖춘, 북유럽 항구 색(팔루 레드, 오커, 화이트, 페일 블루, 민트, 핑크)의 박공지붕 집들이 세 줄로 늘어서 있다. 녹청색 첨탑을 얹은 흰 교회와 둥근 나무 몇 그루도 있다.
- 잡석 방파제가 콘크리트 상부 벽과 함께 어항을 보호한다. 마루 근처에는 큰 바위가 놓여 있고, 작은 해초 낀 바위들은 수중으로 굴러떨어진다. 그 머리 부분에는 갤러리와 유리 등명기를 갖춘 적백 줄무늬 등대가 서 있고, 등불은 천천히 회전한다.
- 열린 수역에는 계류줄에 매달려 흔들리는 작은 요트 두 척(흰 선체, 색색의 스트라이프, 젤리를 통해 보이는 핀 킬, 감아 놓은 돛), 노란 계류 부표, 녹색 항로 부표가 있으며, 모두 파도 위에서 오르내린다.
- 바닥에는 실트가 쌓인 옅은 모래, 해초 다발, 돌, 불가사리, 사슬이 달린 오래된 닻, 잃어버린 타이어가 있다.
배의 하루 (완전 자동 루프, 약 100초)
1. 작은 피더 컨테이너선(네이비 상부, 적색 방오 도료, 선미의 흰 거주구와 브리지 윙, 노란 띠가 있는 굴뚝, 회전하는 레이더, 보이는 프로펠러)이 갑판에 컨테이너를 실은 채 접안해 있다, 6 베이 × 4 열 × 2 단.
2. 레일 위의 선박-육상 갠트리 크레인 두 대, 하나는 빨강, 하나는 파랑이며, 각각 포털 레그, 기계실이 있는 백리치, 포어스테이가 달린 A-프레임, 힌지 붐, 매달린 캡이 있는 트롤리, 네 가닥 와이어 로프에 매달린 노란 스프레더를 갖추고 있다. 붐을 내리고 박스를 교환한다: 선박 적재 더미 꼭대기에서 야드 더미로 하역한 다음, 다른 박스를 배에 싣는다. 모든 동작은 매끄러운 연속이다: 주행, 트롤리 전진, 박스 위로 하강(배의 오르내림 추적), 래치, 권상, 트롤리 횡이동, 하강, 래치 해제. 크레인은 레그 폭보다 가까이 접근하지 않으며, 유휴 크레인은 다른 크레인의 길을 비켜 준다.
3. 작업이 끝나면 트롤리가 주차되고, 붐이 위로 젖혀지며, 예인선이 뱃머리 쪽으로 돌아온다. 예인선은 검은 선체, 흰색과 빨간색 상부 구조, 둘레를 두른 타이어 펜더, 커다란 뱃머리 펜더, 굴뚝 연기 자욱을 갖고 있다. 예인선은 견인 로프를 건네고 뒤로 물러나며, 배가 밖으로 돌아 나오는 동안 뱃머리를 부두에서 끌어낸다. 그다음 로프를 풀고, 예인선은 제자리에서 회전해 돌아가고, 배는 기적을 울린다.
4. 배는 매끄러운 경로를 따라 등대를 지나 항구를 한 바퀴 돈다. 여러 샘플 지점(히브, 피치, 롤)에서 물 위를 타고, 밀어낸 물을 시뮬레이션에 되돌려 주므로 진짜 뱃머리 파도, 항적, 흰 물보라를 남긴다.
5. 그동안 예인선은 대기 지점으로 나간다. 배가 부두 앞에 멈추면 예인선이 다가들어 프로펠러 후류를 휘저으며 배를 옆으로 밀어 펜더에 붙인다. 그다음 물러나고, 붐이 내려오고, 크레인이 다시 시작된다.
- 모든 것이 충돌하지 않게 유지한다(배와 예인선 대 부두, 방파제, 잔교, 요트와 부표; 좌초 없음). 헤드리스 node 테스트로 검증한다.
생명
- 물고기 떼(고등어, 청어, 작은 주황색 놀래기)를 보이드로 구현해 물속과 선체 아래에 머물며, 손가락, 물보라, 움직이는 배에 흩어지고 먹이로 모여든다.
- 달 해파리: 네 개의 분홍 고리, 물결치는 구강완, 촉수 테두리가 있는 유리처럼 맥동하는 우산. 물의 흐름을 따라 표류한다.
- 재갈매기(흰 몸, 회색 등, 검은 날개 끝, 빨간 점이 있는 노란 부리). 선회하고 기울며 활공하다가 횃대에 내려앉는다: 가로등, 지붕 마루, 교회 첨탑, 등대 갤러리, 크레인 꼭대기, 배의 마스트, 예인선, 요트의 마스트헤드. 움직이는 물체 위의 횃대는 그 물체와 함께 움직인다. 기적이 울리면 모두 날아오르고, 일부는 먹이로 급강하한다.
- 기적을 울릴 때 배 굴뚝에서 증기가 뿜어져 나온다; 젤리 속에 갇힌 작은 기포들.
조작 (오른쪽 패널; 휴대폰에서는 하단 시트)
- "Cast off"는 배를 일찍 출항시킨다(크레인은 손에 든 박스를 마저 처리). 바다에 나가면 버튼이 "Call her in"으로 바뀌어 배를 서둘러 귀항시키며, 작업 중에는 회색으로 비활성화된다.
- "Horn"(Space 키도 가능)은 합성된 WebAudio 배 기적을 재생한다: 5도 간격으로 디튠된 두 개의 낮은 톱니파 리드, 로우패스 적용, 사용자 제스처 이후에만 시작된다. 증기도 뿜고 갈매기도 흩어지게 한다.
- "Feed the fish"는 부두 끝에서 펠릿을 뿌린다; 물고기가 몰려들고 갈매기가 급강하한다.
- 집계: Boxes moved / Voyages / Fed. 배 상태 줄("Unloading 3 / 8", "Loading", "Booms up", "Tug coming", "Casting off", "At sea 40%", "Coming in", "Berthing"). 작은 활동 미터.
- 플레이버 스와치가 물 색과 지층을 바꾼다: Sea glass (틸-민트), Blueberry (딥 블루), Lime.
- 슬라이더: Firmness, Wave damping, Tide(±10 cm; 해수면과 배, 떠 있는 물체가 이를 따른다).
- 버튼: Reset, Pause, Sound on/off, Reset view.
포인터 인터랙션
- 물 위를 드래그하면 파도가 생긴다(십자선 커서).
- 블록의 측면을 드래그하면 블록이 기울어진다.
- 주황색과 흰색 구명부환을 집어 던질 수 있다. 구명부환은 파도 위에 평평하게 떠서 부두, 건물, 방파제에 튕기고, 배와 예인선에 밀려난다.
- 빈 공간을 드래그하거나 오른쪽 드래그로 궤도 회전하고, 스크롤이나 핀치로 줌한다.
- 휴대폰에서는 터치로 궤도 회전, 핀치로 줌, 조작을 위한 접이식 하단 시트를 사용한다.
렌더링 세부 사항
- 파이프라인: 섀도 맵 → 코스틱 패스 → 불투명 장면(거리 버퍼 포함) → 물의 백페이스 패스 → 장면 사본 위의 물 패스 → 톤 맵 + 디더.
- 움직이는 리그는 정점이 오브젝트 id를 지닌 정적 정점 버퍼이다; 약 24개의 오브젝트 변환(위치 + 쿼터니언)이 유니폼 버퍼에 들어 있다. 케이블은 한쪽 끝이 각 오브젝트의 프레임에 있어 늘어난다: 크레인 권상 로프, 포어스테이, 견인 로프.
- 프롭 셰이더의 재질 종류: 무광 페인트, 유리, 광택 강철, 지붕 기와, 목재 결, 골판(노멀 교란된 골), 다듬은 석재 벽, 고무, 암석, 빛나는 램프, 흔들리는 해초, 그리고 부트톱 라인 아래 방오 도료를 칠한 선체.
- 젤리의 전단과 스쿼시 흔들림은 블록 안의 모든 것에 적용된다.
품질 기준
- 노트북에서 매끄럽게; 프레임당 시뮬레이션은 약 2 ms 이하.
- WebGPU가 없거나 어댑터가 실패하면 친절한 폴백 화면을 표시한다.
- 콘솔 오류 없음, 페이지 스크롤 없음. prefers-reduced-motion을 존중한다.
- 안무 시나리오의 헤드리스 테스트와 Playwright 엔드투엔드 테스트를 포함해 완성한다.