枠の中でスクロール・クリックして動きを試せます。端末指定は実寸表示です。幅が足りない場合は横にスクロールできます。
再現プロンプト
架空の形状デザインサービスMORPHIC。スクロールに沿って移動する玉虫色の立体に加え、3プリセットと深さ・細かさ・色・回転を調整できる実際の3Dスタジオを備える。調整前との比較、バリエーション、設定の保存と復元、選択中の形を保持したプラン作成へつながる。 配色・構成・動きの数値をまとめた指示書です。 お使いのAIに貼り、素材やお店の情報を伝えて実装してください。仕上がりは利用するAIと素材によって変わります。
Build a complete English site for the fictional product MORPHIC. Its signature is an iridescent, noise-deformed Three.js form that travels through the page, followed by a working shape studio. Deliver a self-contained HTML file with inline CSS and JavaScript. Use locally installed Three.js 0.185.1 and matching local GSAP/ScrollTrigger 3.15.0. Do not add CDN dependencies. No external images or font downloads are needed. Native window scrolling drives the standalone implementation.
## Art direction
Use a calm dark editorial layout: #0d0b14 background, #f3f0f7 text, #c4bece body, #b4acc1 secondary copy, #cdb3fa lavender accent. Keep the iridescence on the form. Primary buttons use #e5d7fb with dark text; secondary buttons have quiet #655a76 borders. Corners are 3–6px. Avoid multicolour pill buttons, repeated glow effects and invented popularity badges.
Desktop headings use a plain sans serif, 64–112px hero type at .97 line-height and tight negative tracking; section headings are 40–76px. Body is 14–16px, line-height 1.75–1.9. Small uppercase monospace labels provide structure. Use generous space around one large form. The full-viewport transparent background canvas sits behind scrolling HTML. Transparent section washes preserve depth; the studio itself has an opaque #14111c background to frame its separate preview cleanly.
The navigation includes the MORPHIC wordmark, Approach, Shape studio, Plans, Motion on/off and Open studio. At phone widths, use two rows and keep all actions visible. All interactive targets are at least 44px tall; focus rings are 2px lavender with 5px offset. Reserve space for the navigation above section targets and hero copy. Use one native scrolling container consistently. A 2px progress line reflects actual scroll position.
## Content and completed actions
1. Hero: eyebrow “An exploration of form & light”; h1 “Shape,” / “with feeling.”; body “Soft folds. Shifting colour. A form that responds to your hand. Find the shape that feels like your next idea.”; primary “Start morphing” opens the studio section and transfers focus. Note “Three starting forms. Your own point of view.” Desktop copy occupies the right 45% with the form at left. On phones, give the form an upper viewing area and place the copy below it. Keep the hero content in normal flow so it grows at large text sizes.
2. Approach: heading “One form.” / “A different feeling.” Three editorial rows introduce Silk, Tide and Alloy with the descriptions and example uses in the dataset below. Each Explore action selects that preset and opens the studio. Use a small index, clear title, supporting copy and a text link; do not turn the rows into empty cards.
3. Studio: heading “Make it your own.” Explain: “Choose a starting form. Adjust one thing at a time. Save the settings when the shape feels right.” Implement the real controls and save/restore flow described below.
4. Plans: heading “From a study” / “to a system.” Spark at $0/month is the individual study with three starting forms, four controls, settings export/restore and a local planning brief. Flux at an illustrative $39/month describes a proposed team service: shared presets, review history, team handover and defining needed output formats. Mark these as sample plans before selection; team features are proposed, not active capabilities. Both buttons open the working plan flow and preselect the exact chosen plan.
5. Footer: “Find your form.”; “A small change in the surface. A different feeling for the whole experience.”; Back to your shape returns to the preserved studio settings. Include the wordmark, “A fictional product. A working shape study.” and a project-brief action.
Do not describe this implementation as neural generation, a prompt-to-model AI, a timed render service or a production model exporter. It is a real procedural shader study with settings export. There is no prompt input, upload-to-model service, production GLB export, image renderer or subscription backend. Keep the copy aligned with what the user can actually do.
## Shape studio
At desktop sizes, use a bordered two-column workbench: a generous square 3D viewport and a 300–360px control panel. A quiet top bar names the currently displayed shape and its four-digit variation number. Beneath the viewport, place playback status and a native “Compare starting preset” checkbox. The controls have native radio presets and four labelled range inputs with current numeric outputs and brief help text. The displayed units for Colour and Turn are degrees; Depth and Detail show two decimals.
The complete preset and control definitions are:
{
"presets": [
{
"id": "silk",
"name": "Silk",
"description": "Soft folds. A quiet, continuous surface.",
"use": "A calm backdrop for a product or an editorial opening.",
"settings": {
"preset": "silk",
"depth": 0.22,
"detail": 0.8,
"hue": 0,
"turn": 0,
"seed": 12
}
},
{
"id": "tide",
"name": "Tide",
"description": "Broader waves with a cool, luminous edge.",
"use": "An expressive centrepiece for a studio or a campaign.",
"settings": {
"preset": "tide",
"depth": 0.48,
"detail": 1.15,
"hue": 105,
"turn": -25,
"seed": 71
}
},
{
"id": "alloy",
"name": "Alloy",
"description": "Smaller ridges. A sharper play of light.",
"use": "A more intricate study for a digital collection.",
"settings": {
"preset": "alloy",
"depth": 0.32,
"detail": 1.8,
"hue": 250,
"turn": 35,
"seed": 104
}
}
],
"controls": [
{
"key": "depth",
"label": "Depth",
"min": 0,
"max": 0.55,
"step": 0.01,
"description": "From a smooth sphere to pronounced folds."
},
{
"key": "detail",
"label": "Detail",
"min": 0.5,
"max": 2,
"step": 0.05,
"description": "From broad waves to smaller ridges."
},
{
"key": "hue",
"label": "Colour",
"min": 0,
"max": 360,
"step": 1,
"description": "Turn the iridescent palette around the colour wheel."
},
{
"key": "turn",
"label": "Turn",
"min": -180,
"max": 180,
"step": 1,
"description": "Rotate the form; you can also drag it sideways."
}
]
}
Selecting a preset copies its full settings into the editable draft and clears comparison. Adjusting any slider updates the actual shader or rotation immediately and returns to the edited view. A new variation increments the integer seed, wrapping 9999 to 1, while retaining the other settings. Reset restores the selected starting preset. Keep all controls usable with motion off.
Comparison renders the selected preset without discarding the edited settings. Pause the preview's animation clock during comparison so the before/after is readable. Clearing comparison restores the edited settings. The label must distinguish “Starting preset” from “Your shape”. Comparison is temporary: Save my settings and Plan with my shape always use the edited draft, including its variation. Explain that edits are saved even while comparing; never replace the saved draft with the reference preset.
Horizontal pointer dragging turns the form. Capture the pointer, map one viewport width to 360 degrees, wrap to -180…180 and round to a degree. Release on pointerup, pointercancel and lostpointercapture. Use touch-action: pan-y so a vertical touch gesture can scroll. The Turn slider provides the equivalent keyboard and touch action; dragging is never the sole control.
On phones, stack the workbench. Keep its preview sticky below the two-row navigation while adjusting controls. The actual picture is 180–260px tall, bounded by viewport height; avoid allowing a large square preview to leave no room for the controls. Use overflow: clip rather than an overflow ancestor that prevents sticky positioning. Test small landscape viewports and focus scrolling as part of visual acceptance.
Save my settings downloads the edited draft as a small UTF-8 JSON file named morphic-PRESET-SEED.json. Its exact schema is { format: "morphic-shape", version: 1, settings: { preset, depth, detail, hue, turn, seed }, note: "Procedural shape settings for the MORPHIC browser study. This is a settings file, not a 3D model or an animation recording." }. The settings specify the surface; they do not capture a particular frame of the moving animation. On download failure retain the entire selectable text. A requested download is not proof that the file was written.
A native details section “Restore saved settings” exposes a labelled textarea and Restore this shape button. Accept at most 16,000 characters. Validate JSON, exact format and version, known preset, finite numeric values, each control's min/max/step and integer seed 1–9999. Reject strings posing as numbers, unknown versions and out-of-range values with a field-linked error; retain the existing shape and pasted text. Copy only the known settings fields. On success restore every setting, clear comparison and announce the preset/variation. Nothing is uploaded.
All plan CTAs carry the edited shape's preset name, variation, Depth, Detail, Colour and Turn into the reviewable and downloadable brief. Preserve the exact selected plan. Edit, close and return actions must not reset the studio draft. Match the dark page with a #14111c dialog, #f3f0f7 text, lavender accent and a legible pale-red error colour. The shared action-flow specification below supplies the actual plan details and validation.
## Real geometry and shader
Use an IcosahedronGeometry of radius 1.4 and detail 48. With this version of Three.js this is 48,020 triangles and 144,060 non-indexed vertices. Keep the existing smooth, recomputed surface normals. A PerspectiveCamera has FOV 38, near .1, far 20 and position (0,0,6.2). Render with alpha and antialiasing, pixel ratio at most 2; explicitly opted-in TOP previews may cap it at 1. Do not claim an unmeasured frame rate.
The studio and the page backdrop use the exact same geometry and shaders, with separate viewport ownership. For studio framing, centre the mesh and set uniform scale to min(1,width/height) × .79, fitting the largest allowed deformation inside the short viewport dimension. User Turn contributes a Y rotation; animation adds time × .05 radians. Studio pointer dragging changes Turn instead of adding a second autonomous pointer tilt.
Uniform mapping is direct: uAmp = depth, uDetail = detail, uHueRotate = hue × PI / 180; uSeed = [((seed×17)%251)/17, ((seed×31)%241)/19, ((seed×47)%239)/23]. This deterministic seed changes the noise location without replacing the geometry. uColorA, B and C use linear working-space THREE.Color values of #9575CD, #4DD9EE and #FF7AC6. Use MeshPhysicalMaterial with roughness .18, metalness 0, clearcoat .8/.12, iridescence 1, iridescenceIOR 1.35, thickness range [120,480] and envMapIntensity 1. Bake RoomEnvironment using PMREM once per visible renderer, environmentIntensity .8; dispose room, target and generator. ACES exposure 1.1, sRGB output. Keep key intensity 2 at (4,6,8), cyan rim 1.2 at (-4,-1,-3). Studio only: PCFShadowMap 1024², bias -.0003, normalBias .02, radius 2, bounds ±3, near .1/far 20; ShadowMaterial floor at -1.65 with opacity .12. A custom depth material applies the identical displacement to its shadow. Background only, outside embedded previews: half-resolution UnrealBloomPass strength .25, radius .6, threshold .85. Render linear HDR scene to a 4-sample HalfFloat target, downsample to a half-sized input, and add its bloom texture inside OutputPass so only one final pass runs at full resolution. Preserve premultiplied alpha and dither output at 1/255. No postprocessing in the comparison studio. Update the camera world matrix before the first unproject so a single reduced-motion draw places the form at the held pose.
Use the following onBeforeCompile snippets; the column-major GLSL hue matrix is multiplied as color * matrix so the RGB transform has the intended row coefficients.
Prepend to the physical vertex shader:
uniform float uTime;
uniform float uAmp;
uniform float uDetail;
uniform vec3 uSeed;
varying float vMorphicNoise;
vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec4 permute(vec4 x) { return mod289(((x * 34.0) + 1.0) * x); }
vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }
float snoise(vec3 v) {
const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0);
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
vec3 i = floor(v + dot(v, C.yyy));
vec3 x0 = v - i + dot(i, C.xxx);
vec3 g = step(x0.yzx, x0.xyz);
vec3 l = 1.0 - g;
vec3 i1 = min(g.xyz, l.zxy);
vec3 i2 = max(g.xyz, l.zxy);
vec3 x1 = x0 - i1 + C.xxx;
vec3 x2 = x0 - i2 + C.yyy;
vec3 x3 = x0 - D.yyy;
i = mod289(i);
vec4 p = permute(permute(permute(
i.z + vec4(0.0, i1.z, i2.z, 1.0))
+ i.y + vec4(0.0, i1.y, i2.y, 1.0))
+ i.x + vec4(0.0, i1.x, i2.x, 1.0));
float n_ = 0.142857142857;
vec3 ns = n_ * D.wyz - D.xzx;
vec4 j = p - 49.0 * floor(p * ns.z * ns.z);
vec4 x_ = floor(j * ns.z);
vec4 y_ = floor(j - 7.0 * x_);
vec4 x = x_ * ns.x + ns.yyyy;
vec4 y = y_ * ns.x + ns.yyyy;
vec4 h = 1.0 - abs(x) - abs(y);
vec4 b0 = vec4(x.xy, y.xy);
vec4 b1 = vec4(x.zw, y.zw);
vec4 s0 = floor(b0) * 2.0 + 1.0;
vec4 s1 = floor(b1) * 2.0 + 1.0;
vec4 sh = -step(h, vec4(0.0));
vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;
vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww;
vec3 p0 = vec3(a0.xy, h.x);
vec3 p1 = vec3(a0.zw, h.y);
vec3 p2 = vec3(a1.xy, h.z);
vec3 p3 = vec3(a1.zw, h.w);
vec4 norm = taylorInvSqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));
p0 *= norm.x;
p1 *= norm.y;
p2 *= norm.z;
p3 *= norm.w;
vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);
m = m * m;
return 42.0 * dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3)));
}
float displacement(vec3 p) {
float n1 = snoise(p * 1.6 * uDetail + uSeed + vec3(0.0, 0.0, uTime * 0.15));
float n2 = snoise(p * 3.4 * uDetail + uSeed + vec3(11.3, 7.1, uTime * 0.28)) * 0.35;
return (n1 + n2) * uAmp;
}
Replace beginnormal_vertex:
vec3 p = normalize(position);
float e = 0.015;
vec3 tangent = normalize(cross(p, abs(p.y) < 0.99 ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0)));
vec3 bitangent = normalize(cross(p, tangent));
vec3 pT = normalize(p + tangent * e);
vec3 pB = normalize(p + bitangent * e);
float baseR = 1.4;
float centreNoise = displacement(p);
vec3 displaced = p * (baseR + centreNoise);
vec3 displacedT = pT * (baseR + displacement(pT));
vec3 displacedB = pB * (baseR + displacement(pB));
vec3 newNormal = normalize(cross(displacedT - displaced, displacedB - displaced));
if (dot(newNormal, p) < 0.0) newNormal = -newNormal;
vMorphicNoise = centreNoise;
vec3 objectNormal = newNormal;
Replace begin_vertex with vec3 transformed = displaced;
Prepend to the physical fragment shader:
uniform vec3 uColorA;
uniform vec3 uColorB;
uniform vec3 uColorC;
uniform float uHueRotate;
varying float vMorphicNoise;
vec3 hueRotate(vec3 color, float angle) {
float c = cos(angle);
float s = sin(angle);
mat3 m = mat3(
0.299 + 0.701 * c + 0.168 * s, 0.587 - 0.587 * c + 0.330 * s, 0.114 - 0.114 * c - 0.497 * s,
0.299 - 0.299 * c - 0.328 * s, 0.587 + 0.413 * c + 0.035 * s, 0.114 - 0.114 * c + 0.292 * s,
0.299 - 0.300 * c + 1.250 * s, 0.587 - 0.588 * c - 1.050 * s, 0.114 + 0.886 * c - 0.203 * s
);
return clamp(color * m, 0.0, 1.0);
}
After color_fragment:
float fresnel = pow(1.0 - max(dot(normalize(vNormal), normalize(vViewPosition)), 0.0), 2.2);
float t = clamp(fresnel + vMorphicNoise * 0.6, 0.0, 1.0);
vec3 irid = t < 0.5
? mix(uColorA, uColorB, t * 2.0)
: mix(uColorB, uColorC, (t - 0.5) * 2.0);
irid = hueRotate(irid, uHueRotate);
diffuseColor.rgb *= irid;
After lights_physical_fragment, set material.iridescenceThickness = mix(120.0,480.0,clamp(.5+vMorphicNoise,0.0,1.0)). Retain the built-in tone mapping and colour-space chunks.
## Page motion and lifecycle
The background pose is screen-space {x,y,scale}. Measure the five actual section positions against the available scroll distance, then interpolate these waypoints: hero [-.42,.05,.76], approach [.48,.1,.6], studio [0,0,.55], plans [0,.1,.5], footer [0,0,.42]. Project NDC x/y onto world Z=0 with the camera. At widths under 760px, use centre X, NDC Y .25 and scale min(.58,width/height × .82), keeping hero text below the form.
Maintain a checkpoint with time, x, y, scale and pointer tilt. For each delta, clamp to 0–.05 seconds and use damping 1-(1-base)^(delta×60): base .045 for position, .05 for scale, .04 for pointer tilt. Mouse-only pointer tilt is limited to .2 radians X and .3 Y. A zero-delta frame leaves the checkpoint unchanged. Paused user edits still update material settings and Turn without advancing time.
Animate only headings once, from opacity .7 and letter-spacing .015em to opacity 1 and -.045em over .9 seconds, power2.out. Do not hide paragraphs, controls, CTAs or pricing cards while waiting for an entrance. All HTML content is available before WebGL is ready.
Use IntersectionObserver for the actual canvas hosts. Do not allocate a renderer until its surface has a nonzero visible intersection and the document is visible. Resize and render a candidate's first frame before attaching it or reporting readiness. Catch renderer, texture, shader, first-render, resize and later-render failures. Handle context loss during construction and after first paint. A failure must retain the HTML and draft, dispose partial resources and expose an English retry control; do not automatically retry on every frame or replace the entire site with an error boundary.
The backdrop stops drawing while the studio is visible, so only the useful form is animated. Each surface releases GPU resources when it leaves the viewport or the tab is hidden. Keep its checkpoint and current settings, then recreate the same geometry at delta 0 on re-entry. Ignore stale callbacks from older renderers. Redraw current parameters after a paused surface resumes from suspension; edits made while hidden should be applied on its next visible frame. Never advance time to account for hidden seconds.
Listen to OS reduced-motion changes and provide a manual Motion switch. The OS preference takes precedence and is explained on the disabled control. Stop GPU RAF, automatic turn, background travel, heading motion and scroll smoothing while motion is off. Section navigation becomes immediate with focus transfer. The studio remains directly adjustable and redraws only when input or size changes. Open dialogs suspend both scenes. Resume only when visible and enabled. Cleanup must cancel RAF, remove pointer and visibility handlers, disconnect observers and dispose every geometry, material and renderer, even when a context is already lost.
## Acceptance
Verify preset selection, all four controls and drag/keyboard equivalence against the actual GPU, comparison without lost edits, reset/variation, exact settings save/restore, malformed-file recovery, plan preselection with shape values, focus restoration and pointer cancellation. Inspect 320px, landscape and larger text layouts, sticky preview with sliders, OS/manual reduced motion, hidden-tab return and independent GPU retry. Confirm all content stays usable without WebGL. Node data and lifecycle tests cannot establish final visual quality or end-to-end browser acceptance.
## Action flow and content
Implement the following local planning experience for the primary CTA, in the visual language of the page. Use a real button opening a labelled modal dialog. Choosing a pricing card must preselect that exact plan. A navigation link may move to another section; a planning CTA must lead to its review flow.
Use this content for the dialog, including the option details and notes:
{
"slug": "morph-blob",
"brand": "MORPHIC",
"accent": "#cdb3fa",
"paper": "#14111c",
"ink": "#f3f0f7",
"colorScheme": "dark",
"title": "Keep a shape brief",
"intro": "Keep the chosen form and the intended use together, then define the next step.",
"facts": [
[
"Spark",
"The browser studio: three starting forms, four controls, settings export and restore."
],
[
"Flux",
"A proposed team workflow: shared presets, review history and handover."
],
[
"Next step",
"Describe the intended audience, setting and output you would want to evaluate."
]
],
"groups": [
{
"label": "Plan",
"options": [
{
"title": "Spark",
"detail": "Individual exploration · sample price $0 per month."
},
{
"title": "Flux",
"detail": "Proposed team workflow · sample price $39 per month."
}
]
}
],
"brief": "Describe where the form will appear, who will see it and what it should communicate.",
"notes": [
"The MORPHIC studio saves procedural settings. Flux is an illustrative team-service proposal. This brief does not create a subscription or deliver a 3D model."
]
}
- Explain before input that this is a fictional brand sample: choices can be reviewed and saved, with no external submission, booking, payment or registration. Use each brand's language. Do not launch a mail client or present an installer for a fictional product.
- Give every option group a legend, inputs explicit labels and errors linked descriptions. Validate options against the allowed values, requested dates as actual dates from today onward, and free text as 10–2,000 trimmed characters. Blank spaces alone must fail. Tracking accepts the sample reference TG-2048 only.
- Focus the first invalid field and preserve every entered value. On success, show the selected option WITH its full detail, requested date or brief, relevant facts and notes. An edit action returns to the preserved form. Do not claim a real reservation, purchase or account has succeeded.
- Save the complete review as UTF-8 text. A browser download request is not proof of a file being written. On download failure, retain the full text in a labelled, selectable textarea and offer retry. Do not show a false success state.
- Constrain modal content to the viewport with internal scrolling. Support keyboard and touch, trap focus while open, close on Escape and restore focus to the exact initiating control. Keep these operations available with motion disabled.
- If a separate project-contact CTA is present, let it collect a project brief and review it locally; a product selection CTA must retain the product choice. Verify both flows independently.