Create "Melon Jelly Knife" — a single self-contained HTML file (melon-jelly-knife.html) with an interactive, physically simulated gummy watermelon slice that you can pull like jelly AND cut into pieces with a visible steel knife that animates every cut.
── Platform ──
- WebGPU + WGSL only, all JS/CSS inline, no external libraries. ES module in one <script type="module">.
- If navigator.gpu / adapter is missing, show an elegant editorial fallback card ("This specimen needs WebGPU.") with a specific explanation — never fake it with a lesser renderer. Disable the controls in that state.
- Recover from device loss (rebuild the renderer up to twice), surface WGSL compile errors via getCompilationInfo.
- Fixed 60 Hz simulation step with an accumulator (max 3 steps/frame), ¼-speed toggle, pause (Space), keyboard: N nudge, R reset, K toggle knife, H hand.
── Page design (editorial "material study") ──
- Warm paper studio backdrop #E2DFDA; the 3D floor is an infinite studio sweep that tones to exactly this colour.
- Top-left masthead: small caps label "MATERIAL STUDIES / NO. 009", huge italic serif title "Melon / Jelly." (second line indented), and a 3-line serif tagline.
- Top-right status pill "WEBGPU · LIVE / PAUSED / UNAVAILABLE".
- Right panel (thin 1px borders, translucent paper): Tool (Hand | Knife segmented buttons with small line icons), Variety swatches (Crimson, Golden, Rosé — each recolours flesh, rind, seeds, shadow tint and the UI accent), sliders Firmness (trembling ↔ set) and Internal damping (lively ↔ syrupy), buttons "Give it a nudge", Reset, Pause, checkboxes ¼ speed and Show mesh.
- Bottom-left: tool hint in italic serif, live readouts row (Mass ≈ g, Volume % of rest, Kinetic µJ, Pieces) with an "illustrative scale" footnote (1 sim unit ≈ 3.5 cm, gummy 1.3 g/cm³).
- Bottom-right collapsible "Inside the experiment" notes explaining the physics, rendering and the cutting method in plain prose.
- A dashed SVG guide line while drawing a knife stroke; small toast messages ("Missed — draw the blade across the slice.", "Too thin to cut there.", "That's plenty of pieces.").
- The camera frames the slice inside the free area between masthead, panel and readouts (lens shift), orbits on empty-space drag, zooms on wheel, double-click resets view, gently follows the slice if it is thrown far.
- Responsive: under ~860px the layout stacks (masthead, canvas ~58vh, hint, panel) with 44px+ touch targets; prefers-reduced-motion softens the intro and damping.
── The slice ──
- A thick (T ≈ 0.58) wedge of watermelon: sector with ~32° half-angle, inner radius ~1.62, all corners rounded in plane (ρ = 0.17) and top/bottom edges bevelled (~0.13).
- Layers by depth from the rind: dark green skin with irregular darker stripes, a pale cream-green band, then translucent red flesh; black teardrop seeds arranged in arcs and a few tiny air bubbles suspended inside.
- Rendered as translucent candy: screen-space refraction of the scene behind, per-pixel thickness from a back-face depth pass, Beer–Lambert absorption per refracted tap (capped by back thickness so seeds don't halo), jittered blur, subsurface-like scatter and glow toward the key light, Fresnel + GGX highlights, soft studio environment (procedural softboxes).
- Floor: PCSS-lite soft shadow from a key light behind-left-above (shadow tinted by the flesh colour), contact AO from a top-down height map. MSAA ×4, HDR, PBR-Neutral tone mapping, sRGB, dither.
── Physics (feels like real gummy candy) ──
- Tetrahedral soft body (Delaunay of a jittered lattice inside the shape), XPBD with small steps (~10 substeps, 1 iteration each):
• co-rotational per-tet shape matching (mass-weighted covariance, Müller quaternion rotation extraction, uniform stiffness fraction) — no inversions, no ghost torque;
• per-tet volume constraints (near-incompressible);
• edge relative-velocity damping controlled by the damping slider; firmness maps to shape compliance on a log scale;
• floor contact with static/kinetic friction, light air drag, settle-damping when nearly at rest, speed cap, NaN guard with gentle reset.
- Hand tool: grab anywhere (ray pick on the skinned surface, forgiving screen-space fallback), soft patch attachment (radius ~0.4) that follows the pointer on a camera-facing plane, reach limit, never pushed into the floor; wheel or a second finger twists the held patch. Nudge = a playful impulse. Reset drops the whole slice again.
- Render mesh is skinned to the tets by barycentric embedding; normals recomputed each frame.
── Cutting into pieces ──
- The world is a list of pieces. Each piece is a convex polygon I in the slice's rest plane; its outline is I dilated by a disk of radius ρ (rounded corners), extruded to the slice thickness with the same bevels, rind layers, seeds and bubbles (seeds/bubbles kept only where they fit inside).
- A cut maps a world plane into each crossed piece's rest frame (best-fit rigid frame of the deformed piece), intersects it with the mid-plane → a straight line; clip I into two halves offset by ±ρ so both rebuilt outlines meet exactly at the cut; reject slivers ("too thin").
- All pieces are concatenated into ONE simulation (component id per particle) and ONE render mesh; each piece is its own soft body you can grab, stretch and cut again; pieces collide with each other (AABB-pruned particle–particle pushes, a few mm per pass so fresh faces ease apart instead of popping). Max 14 pieces. Built meshes are cached per piece so Reset is instant.
- State transfer on every rebuild: new rest particles are embedded in the old tets and take over interpolated positions and velocities.
── The visible knife (the star of this version) ──
- A small nakiri-style vegetable knife modelled procedurally: rectangular blade (length ~1.72, height ~0.63 — taller than the slice), straight edge with a slight lift and rounded nose at the front, thin ground bevel along the edge, flat grind to a ~0.03 spine, sharp creases (duplicated vertices), polished steel bolster, oval walnut handle that swells slightly with three steel rivets and flat end caps. Local frame: +x toward the tip, edge on y=0.
- Materials in the scene shader: brushed steel with a product-shot gradient (dark board reflected low, bright sweep high), softbox streaks, satin lighter bevel, mirror bolster; walnut with straight grain, satin oil sheen, rivets. Posed rigidly on the CPU each frame.
- The knife is drawn in the scene pass (behind the jelly surface), so the part buried in the jelly is seen refracted through it; it casts a shadow but stays out of the AO height map.
- Behaviour:
• Hover (knife tool): the knife floats ~0.42 above the slice with its edge exactly under the cursor, blade side-on to the camera, slightly rolled toward the viewer; it drops in from above when the tool is selected; hidden in hand mode.
• While drawing: it lines up along the stroke, edge just above the jelly, handle always on the left (canonical direction relative to camera right).
• On release: the stroke read on the surface it was drawn over defines a VERTICAL blade plane; the split is planned and the new pieces + new soft body are pre-built immediately (so the swap later costs only ~10 ms and there is no hitch mid-animation).
• Choreography (~1.1 s, respects ¼ speed and pause): align (0.16 s) → press: the edge sinks ~0.13 below the top and a physical collider pushes the jelly into a rounded groove under it — it squashes before it gives (to 0.42 s) → break-through: the new world is swapped in, the blade continues to the board while a wedge collider eases the two new faces apart (limited to a few mm per substep, gap growing with depth) and holds the cut lips down at the break-through depth → short hold → lift back to hover, releasing the lips so they spring back with a jiggle.
• Misses show a toast and the knife stays put; new strokes are ignored while a cut is animating; Reset cancels it.
── Quality bar ──
- Smooth 60 fps on a mid-range laptop GPU; no allocations in the frame loop except on cuts.
- Deterministic test hook on window (e.g. __melon with sim, camera, pick, planCut, startCut, advance(seconds), per-piece stats) so the animation can be stepped and screenshotted.
- Polished, calm, museum-like typography; everything readable on phone and desktop.
한국어 번역
"Melon Jelly Knife"를 만드세요 — 단일 자체 완결형 HTML 파일(melon-jelly-knife.html)로, 상호작용 가능하고 물리 시뮬레이션되는 젤리 수박 조각을 담습니다. 이 조각은 젤리처럼 잡아당길 수 있고, 동시에 매번 절단 동작이 애니메이션되는 눈에 보이는 강철 칼로 조각조각 자를 수 있습니다.
── 플랫폼 ──
- WebGPU + WGSL만 사용하고, 모든 JS/CSS는 인라인으로, 외부 라이브러리는 사용하지 않습니다. ES 모듈은 하나의 <script type="module"> 안에 둡니다.
- navigator.gpu / adapter가 없으면 구체적인 설명과 함께 세련된 에디토리얼 대체 카드("This specimen needs WebGPU.")를 표시하세요 — 결코 하위 렌더러로 흉내 내지 마세요. 그 상태에서는 컨트롤을 비활성화합니다.
- 디바이스 손실에서 복구하고(렌더러를 최대 두 번 재구축), getCompilationInfo를 통해 WGSL 컴파일 오류를 드러내세요.
- 누산기를 사용한 고정 60 Hz 시뮬레이션 스텝(프레임당 최대 3스텝), ¼ 속도 토글, 일시정지(Space), 키보드: N 넛지, R 리셋, K 칼 토글, H 손.
── 페이지 디자인 (에디토리얼 "material study") ──
- 따뜻한 종이 질감의 스튜디오 배경 #E2DFDA; 3D 바닥은 정확히 이 색으로 이어지는 무한 스튜디오 스윕입니다.
- 좌측 상단 마스트헤드: 스몰캡 라벨 "MATERIAL STUDIES / NO. 009", 거대한 이탤릭 세리프 제목 "Melon / Jelly."(둘째 줄 들여쓰기), 3줄짜리 세리프 태그라인.
- 우측 상단 상태 필 "WEBGPU · LIVE / PAUSED / UNAVAILABLE".
- 우측 패널(얇은 1px 테두리, 반투명 종이): Tool(작은 라인 아이콘이 있는 Hand | Knife 세그먼트 버튼), Variety 스와치(Crimson, Golden, Rosé — 각각 과육, 껍질, 씨앗, 그림자 색조와 UI 액센트를 다시 채색), 슬라이더 Firmness(trembling ↔ set)와 Internal damping(lively ↔ syrupy), 버튼 "Give it a nudge", Reset, Pause, 체크박스 ¼ speed와 Show mesh.
- 좌측 하단: 이탤릭 세리프의 도구 힌트, 실시간 수치 행(Mass ≈ g, Volume % of rest, Kinetic µJ, Pieces)과 "illustrative scale" 각주(1 sim unit ≈ 3.5 cm, gummy 1.3 g/cm³).
- 우측 하단 접이식 "Inside the experiment" 노트: 물리, 렌더링, 절단 방식을 평이한 문장으로 설명.
- 칼 획을 그리는 동안 점선 SVG 가이드 라인; 작은 토스트 메시지("Missed — draw the blade across the slice.", "Too thin to cut there.", "That's plenty of pieces.").
- 카메라는 마스트헤드, 패널, 수치 사이의 여유 공간 안에 조각이 들어오도록 프레이밍하고(렌즈 시프트), 빈 공간 드래그로 회전하며, 휠로 줌, 더블클릭으로 뷰를 초기화하고, 조각이 멀리 던져지면 부드럽게 따라갑니다.
- 반응형: 약 860px 미만에서는 레이아웃이 세로로 쌓이고(마스트헤드, 캔버스 ~58vh, 힌트, 패널), 44px 이상의 터치 타깃을 사용합니다; prefers-reduced-motion은 인트로와 감쇠를 부드럽게 만듭니다.
── 조각 ──
- 두꺼운(T ≈ 0.58) 수박 웨지: 반각 약 32°의 부채꼴, 내부 반경 약 1.62, 평면상의 모든 모서리는 둥글게(ρ = 0.17), 상단/하단 가장자리는 베벨 처리(~0.13).
- 껍질에서 깊이 방향으로의 층: 불규칙한 짙은 줄무늬가 있는 진녹색 껍질, 옅은 크림그린 띠, 그다음 반투명한 붉은 과육; 호를 따라 배열된 검은 물방울 모양 씨앗과 내부에 떠 있는 몇 개의 작은 공기 방울.
- 반투명 캔디처럼 렌더링: 뒤쪽 장면의 스크린 공간 굴절, 백페이스 깊이 패스에서 얻은 픽셀별 두께, 굴절 샘플마다의 Beer–Lambert 흡수(뒤쪽 두께로 상한을 두어 씨앗에 헤일로가 생기지 않게 함), 지터 블러, 키 라이트 방향의 서브서피스 유사 산란과 글로우, Fresnel + GGX 하이라이트, 부드러운 스튜디오 환경(절차적 소프트박스).
- 바닥: 뒤-왼쪽-위에 있는 키 라이트에서 나오는 PCSS-lite 소프트 섀도(그림자는 과육 색으로 착색), 탑다운 높이 맵에서 얻은 접촉 AO. MSAA ×4, HDR, PBR-Neutral 톤 매핑, sRGB, 디더.
── 물리 (진짜 구미 캔디처럼 느껴져야 함) ──
- 사면체 소프트 바디(형상 내부의 지터링된 격자에 대한 Delaunay), 작은 스텝의 XPBD(약 10 서브스텝, 각 1회 반복):
• 사면체별 공회전 형상 매칭(질량 가중 공분산, Müller 쿼터니언 회전 추출, 균일 강성 비율) — 반전 없음, 고스트 토크 없음;
• 사면체별 부피 제약(거의 비압축);
• 감쇠 슬라이더로 제어되는 에지 상대 속도 감쇠; 강성은 로그 스케일의 형상 컴플라이언스로 매핑;
• 정적/동적 마찰이 있는 바닥 접촉, 가벼운 공기 저항, 거의 정지 상태일 때의 안정화 감쇠, 속도 상한, NaN 방어와 부드러운 리셋.
- Hand 도구: 어디든 잡기(스키닝된 표면에 대한 레이 픽, 관대한 스크린 공간 폴백), 카메라를 향한 평면에서 포인터를 따라가는 소프트 패치 부착(반경 ~0.4), 도달 한계, 바닥으로 밀려나지 않음; 휠 또는 두 번째 손가락으로 잡은 패치를 비틀 수 있습니다. 넛지 = 장난스러운 임펄스. 리셋은 조각 전체를 다시 떨어뜨립니다.
- 렌더 메시는 무게중심 임베딩으로 사면체에 스키닝되고, 노멀은 매 프레임 재계산됩니다.
── 조각으로 자르기 ──
- 세계는 조각들의 목록입니다. 각 조각은 슬라이스의 정지 평면에 있는 볼록 다각형 I이며, 그 윤곽은 I를 반경 ρ의 원판으로 팽창시킨 것(둥근 모서리)이고, 동일한 베벨과 껍질 층, 씨앗, 기포를 가진 채 슬라이스 두께로 압출됩니다(씨앗/기포는 내부에 들어맞는 위치에만 유지).
- 절단은 월드 평면을 교차하는 각 조각의 정지 프레임(변형된 조각의 최적 적합 강체 프레임)으로 매핑하고, 중간 평면과 교차시켜 직선을 얻습니다; I를 ±ρ만큼 오프셋한 두 반쪽으로 클리핑하여 재구축된 두 윤곽이 절단면에서 정확히 만나게 합니다; 지나치게 얇은 조각은 거부합니다("too thin").
- 모든 조각은 하나의 시뮬레이션(입자별 컴포넌트 id)과 하나의 렌더 메시로 이어집니다; 각 조각은 잡고 늘리고 다시 자를 수 있는 자체 소프트 바디이며, 조각들끼리 충돌합니다(AABB로 가지치기한 입자–입자 밀어내기, 새로 생긴 면이 튀지 않고 부드럽게 벌어지도록 패스당 몇 mm). 최대 14조각. 구축된 메시는 조각별로 캐시되어 리셋이 즉각적입니다.
- 재구축 시마다 상태 이전: 새로운 정지 입자를 이전 사면체에 임베딩하고 보간된 위치와 속도를 이어받습니다.
── 눈에 보이는 칼 (이번 버전의 주인공) ──
- 절차적으로 모델링한 작은 나키리 스타일 채소칼: 직사각형 블레이드(길이 ~1.72, 높이 ~0.63 — 슬라이스보다 높음), 앞쪽이 약간 들리고 코가 둥근 직선 날, 날을 따라 얇은 연마 베벨, ~0.03 두께의 등으로 이어지는 플랫 그라인드, 날카로운 크리스(중복 정점), 연마된 강철 볼스터, 살짝 불룩하고 세 개의 강철 리벳과 평평한 엔드 캡이 있는 타원형 월넛 손잡이. 로컬 프레임: +x는 끝을 향하고, 날은 y=0.
- 장면 셰이더의 재질: 제품 사진식 그라디언트가 있는 브러시드 스틸(아래쪽은 어두운 보드 반사, 위쪽은 밝은 스윕), 소프트박스 스트릭, 새틴 같은 밝은 베벨, 거울 같은 볼스터; 곧은 결의 월넛, 새틴 오일 광택, 리벳. 매 프레임 CPU에서 강체로 포즈됩니다.
- 칼은 장면 패스에서(젤리 표면 뒤에) 그려지므로, 젤리에 파묻힌 부분은 젤리를 통해 굴절되어 보입니다; 칼은 그림자를 드리우지만 AO 높이 맵에는 포함되지 않습니다.
- 동작:
• 호버(칼 도구): 칼은 슬라이스 위 ~0.42에 떠 있고 날이 정확히 커서 아래에 오며, 블레이드는 카메라에 측면으로, 시청자 쪽으로 살짝 기울어집니다; 도구를 선택하면 위에서 내려오고, 손 모드에서는 숨겨집니다.
• 그리는 동안: 획을 따라 정렬되고, 날은 젤리 바로 위에, 손잡이는 항상 왼쪽에 옵니다(카메라 오른쪽에 대한 정준 방향).
• 놓는 순간: 그 획이 그려진 표면에서 읽어낸 값이 수직(VERTICAL) 블레이드 평면을 정의합니다; 분할이 계획되고 새 조각 + 새 소프트 바디가 즉시 미리 구축됩니다(나중 교체 비용이 ~10 ms에 불과해 애니메이션 중간에 끊김이 없음).
• 안무(~1.1초, ¼ 속도와 일시정지를 존중): 정렬(0.16초) → 누르기: 날이 상단 아래로 ~0.13까지 잠기고 물리 콜라이더가 젤리를 그 아래의 둥근 홈으로 밀어 넣습니다 — 젤리는 버티다가 찌그러집니다(0.42초까지) → 관통: 새 세계로 교체되고, 블레이드는 보드까지 계속 나아가며 웨지 콜라이더가 새로 생긴 두 면을 부드럽게 벌리고(서브스텝당 몇 mm로 제한, 깊이에 따라 간격 증가) 관통 깊이에서 절단 입술을 눌러 고정합니다 → 짧은 유지 → 호버로 다시 들어올리며 입술을 놓아주어 탄성 있게 튕겨 돌아오게 합니다.
• 빗나가면 토스트가 뜨고 칼은 그대로 있습니다; 절단 애니메이션 중에는 새 획이 무시됩니다; 리셋은 이를 취소합니다.
── 품질 기준 ──
- 중급 노트북 GPU에서 부드러운 60 fps; 절단 시를 제외하고 프레임 루프에서 할당 없음.
- window에 결정론적 테스트 훅(예: sim, camera, pick, planCut, startCut, advance(seconds), 조각별 통계를 가진 __melon)을 두어 애니메이션을 단계별로 진행하고 스크린샷을 찍을 수 있게 합니다.
- 세련되고 차분한 박물관 같은 타이포그래피; 휴대폰과 데스크톱 모두에서 읽을 수 있어야 합니다.