glm-5.1 Code
Model-specific source generated from the shared prompt shown on the gallery page. The prompt itself is intentionally not duplicated here.

JavaScript
src/main.js
1import './styles.css';2import * as THREE from 'three';3 4const MODEL_NAME = 'glm-5-1';5 6const app = document.querySelector('#app');7 8const renderer = new THREE.WebGLRenderer({ antialias: true });9renderer.setPixelRatio(window.devicePixelRatio);10renderer.setSize(window.innerWidth, window.innerHeight);11renderer.toneMapping = THREE.ACESFilmicToneMapping;12renderer.toneMappingExposure = 0.9;13app.appendChild(renderer.domElement);14 15const scene = new THREE.Scene();16scene.background = new THREE.Color('#1a2a44');17scene.fog = new THREE.FogExp2('#1a2a44', 0.0012);18 19const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 2000);20camera.position.set(0, 25, 80);21camera.lookAt(0, 0, 0);22 23const vertexShader = `24 uniform float uTime;25 uniform float uWaveHeight;26 uniform float uWaveSpeed;27 28 varying vec3 vWorldPos;29 varying vec3 vNormalCalc;30 varying float vElevation;31 32 void main() {33 vec3 pos = position;34 float t = uTime * uWaveSpeed;35 36 float w1 = sin(pos.x * 0.02 + t * 0.8) * uWaveHeight;37 float w2 = sin(pos.z * 0.03 + t * 0.6) * uWaveHeight * 0.7;38 float w3 = sin((pos.x + pos.z) * 0.015 + t * 1.1) * uWaveHeight * 0.5;39 float w4 = sin(pos.x * 0.05 + pos.z * 0.04 + t * 1.4) * uWaveHeight * 0.3;40 float w5 = sin(pos.x * 0.08 - t * 0.9) * uWaveHeight * 0.15;41 42 pos.y += w1 + w2 + w3 + w4 + w5;43 44 float dx = cos(pos.x * 0.02 + t * 0.8) * 0.02 * uWaveHeight45 + cos((pos.x + pos.z) * 0.015 + t * 1.1) * 0.015 * uWaveHeight46 + cos(pos.x * 0.05 + pos.z * 0.04 + t * 1.4) * 0.05 * uWaveHeight47 + cos(pos.x * 0.08 - t * 0.9) * 0.08 * uWaveHeight;48 49 float dz = cos(pos.z * 0.03 + t * 0.6) * 0.03 * uWaveHeight * 0.750 + cos((pos.x + pos.z) * 0.015 + t * 1.1) * 0.015 * uWaveHeight51 + cos(pos.x * 0.05 + pos.z * 0.04 + t * 1.4) * 0.04 * uWaveHeight;52 53 vec3 n = normalize(vec3(-dx, 1.0, -dz));54 55 vWorldPos = pos;56 vNormalCalc = n;57 vElevation = pos.y;58 59 gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);60 }61`;62 63const fragmentShader = `64 uniform float uTime;65 uniform vec3 uDeepColor;66 uniform vec3 uShallowColor;67 uniform vec3 uFoamColor;68 69 varying vec3 vWorldPos;70 varying vec3 vNormalCalc;71 varying float vElevation;72 73 void main() {74 vec3 lightDir = normalize(vec3(0.5, 0.8, 0.3));75 vec3 viewDir = normalize(cameraPosition - vWorldPos);76 vec3 halfDir = normalize(lightDir + viewDir);77 78 float diff = max(dot(vNormalCalc, lightDir), 0.0);79 float spec = pow(max(dot(vNormalCalc, halfDir), 0.0), 64.0);80 81 float depthFactor = smoothstep(-8.0, 15.0, vElevation);82 vec3 waterColor = mix(uDeepColor, uShallowColor, depthFactor);83 84 float foamThreshold = 7.0;85 float foam = smoothstep(foamThreshold - 1.5, foamThreshold + 1.5, vElevation);86 waterColor = mix(waterColor, uFoamColor, foam * 0.6);87 88 float edgeFoam = smoothstep(foamThreshold + 1.5, foamThreshold - 1.5, vElevation);89 float sparkle = sin(vWorldPos.x * 2.0 + uTime * 1.5) * sin(vWorldPos.z * 2.3 + uTime * 1.2);90 sparkle = smoothstep(0.85, 1.0, sparkle) * edgeFoam;91 waterColor += uFoamColor * sparkle * 0.15;92 93 vec3 color = waterColor * (0.35 + diff * 0.45) + vec3(1.0) * spec * 0.5;94 95 float fogDist = length(vWorldPos - cameraPosition);96 float fogFactor = 1.0 - exp(-0.0012 * fogDist * fogDist * 0.0004);97 color = mix(color, vec3(0.102, 0.165, 0.267), fogFactor);98 99 gl_FragColor = vec4(color, 1.0);100 }101`;102 103const oceanUniforms = {104 uTime: { value: 0 },105 uWaveHeight: { value: 8.0 },106 uWaveSpeed: { value: 1.0 },107 uDeepColor: { value: new THREE.Color('#041830') },108 uShallowColor: { value: new THREE.Color('#0b4f8a') },109 uFoamColor: { value: new THREE.Color('#b8dbe8') },110};111 112const oceanMaterial = new THREE.ShaderMaterial({113 vertexShader,114 fragmentShader,115 uniforms: oceanUniforms,116 side: THREE.DoubleSide,117});118 119const oceanGeometry = new THREE.PlaneGeometry(600, 600, 200, 200);120oceanGeometry.rotateX(-Math.PI / 2);121 122const ocean = new THREE.Mesh(oceanGeometry, oceanMaterial);123scene.add(ocean);124 125const ambientLight = new THREE.AmbientLight('#3a5a8a', 0.4);126scene.add(ambientLight);127 128const dirLight = new THREE.DirectionalLight('#ffffff', 1.2);129dirLight.position.set(50, 80, 30);130scene.add(dirLight);131 132const controls = document.createElement('div');133controls.id = 'controls';134controls.innerHTML = `135 <div class="control-row">136 <label>Wave Height137 <div class="control-row">138 <input type="range" id="wave-height" min="1" max="20" step="0.5" value="8" />139 <span class="value-display" id="wave-height-val">8.0</span>140 </div>141 </label>142 </div>143 <div class="control-row">144 <label>Wave Speed145 <div class="control-row">146 <input type="range" id="wave-speed" min="0.1" max="3.0" step="0.1" value="1.0" />147 <span class="value-display" id="wave-speed-val">1.0</span>148 </div>149 </label>150 </div>151 <button id="pause-btn">Pause</button>152`;153app.appendChild(controls);154 155const pauseBtn = document.getElementById('pause-btn');156const waveHeightInput = document.getElementById('wave-height');157const waveHeightVal = document.getElementById('wave-height-val');158const waveSpeedInput = document.getElementById('wave-speed');159const waveSpeedVal = document.getElementById('wave-speed-val');160 161let paused = false;162 163pauseBtn.addEventListener('click', () => {164 paused = !paused;165 pauseBtn.textContent = paused ? 'Resume' : 'Pause';166});167 168waveHeightInput.addEventListener('input', (e) => {169 const v = parseFloat(e.target.value);170 oceanUniforms.uWaveHeight.value = v;171 waveHeightVal.textContent = v.toFixed(1);172});173 174waveSpeedInput.addEventListener('input', (e) => {175 const v = parseFloat(e.target.value);176 oceanUniforms.uWaveSpeed.value = v;177 waveSpeedVal.textContent = v.toFixed(1);178});179 180const modelLabel = document.createElement('div');181modelLabel.id = 'model-label';182modelLabel.textContent = MODEL_NAME;183app.appendChild(modelLabel);184 185const fpsCounter = document.createElement('div');186fpsCounter.id = 'fps-counter';187app.appendChild(fpsCounter);188 189let elapsedTime = 0;190const clock = new THREE.Clock();191let frameCount = 0;192let fpsTime = 0;193 194function animate() {195 requestAnimationFrame(animate);196 197 const delta = clock.getDelta();198 199 if (!paused) {200 elapsedTime += delta;201 oceanUniforms.uTime.value = elapsedTime;202 }203 204 const camRadius = 120;205 const camSpeed = 0.05;206 camera.position.x = Math.sin(elapsedTime * camSpeed) * camRadius;207 camera.position.z = Math.cos(elapsedTime * camSpeed) * camRadius;208 camera.position.y = 20 + Math.sin(elapsedTime * camSpeed * 0.5) * 8;209 camera.lookAt(0, 0, 0);210 211 frameCount++;212 fpsTime += delta;213 if (fpsTime >= 1) {214 fpsCounter.textContent = `${Math.round(frameCount / fpsTime)} fps`;215 frameCount = 0;216 fpsTime = 0;217 }218 219 renderer.render(scene, camera);220}221 222animate();223 224window.addEventListener('resize', () => {225 camera.aspect = window.innerWidth / window.innerHeight;226 camera.updateProjectionMatrix();227 renderer.setSize(window.innerWidth, window.innerHeight);228});
CSS
src/styles.css
1* {2 box-sizing: border-box;3}4 5html,6body,7#app {8 width: 100%;9 height: 100%;10 margin: 0;11}12 13body {14 font-family: system-ui, -apple-system, sans-serif;15 overflow: hidden;16 background: #0a0e1a;17}18 19#app {20 position: relative;21}22 23canvas {24 display: block;25 width: 100%;26 height: 100%;27}28 29#controls {30 position: absolute;31 bottom: 20px;32 left: 20px;33 background: rgba(0, 0, 0, 0.55);34 backdrop-filter: blur(8px);35 border-radius: 10px;36 padding: 14px 18px;37 color: #e0ecff;38 display: flex;39 flex-direction: column;40 gap: 10px;41 min-width: 220px;42 max-width: 280px;43 border: 1px solid rgba(100, 160, 255, 0.2);44}45 46#controls label {47 font-size: 12px;48 text-transform: uppercase;49 letter-spacing: 0.06em;50 opacity: 0.7;51 display: flex;52 flex-direction: column;53 gap: 4px;54}55 56#controls .control-row {57 display: flex;58 align-items: center;59 gap: 10px;60}61 62#controls input[type="range"] {63 flex: 1;64 accent-color: #4a90d9;65 height: 4px;66}67 68#controls .value-display {69 min-width: 32px;70 text-align: right;71 font-size: 13px;72 font-variant-numeric: tabular-nums;73}74 75#pause-btn {76 background: rgba(74, 144, 217, 0.25);77 border: 1px solid rgba(74, 144, 217, 0.4);78 color: #e0ecff;79 border-radius: 6px;80 padding: 8px 16px;81 cursor: pointer;82 font-size: 13px;83 font-family: inherit;84 transition: background 0.2s;85 letter-spacing: 0.04em;86}87 88#pause-btn:hover {89 background: rgba(74, 144, 217, 0.45);90}91 92#model-label {93 position: absolute;94 top: 14px;95 right: 14px;96 background: rgba(0, 0, 0, 0.45);97 backdrop-filter: blur(6px);98 border-radius: 6px;99 padding: 6px 12px;100 color: rgba(200, 220, 255, 0.7);101 font-size: 11px;102 letter-spacing: 0.04em;103 border: 1px solid rgba(100, 160, 255, 0.15);104}105 106#fps-counter {107 position: absolute;108 top: 14px;109 left: 14px;110 color: rgba(200, 220, 255, 0.45);111 font-size: 11px;112 font-variant-numeric: tabular-nums;113}114 115@media (max-width: 600px) {116 #controls {117 bottom: 10px;118 left: 10px;119 right: 10px;120 max-width: none;121 min-width: 0;122 padding: 10px 14px;123 gap: 8px;124 }125 126 #model-label {127 top: 10px;128 right: 10px;129 font-size: 10px;130 }131 132 #fps-counter {133 top: 10px;134 left: 10px;135 font-size: 10px;136 }137}
Package
package.json
1{2 "name": "ocean-ui-model-run",3 "version": "0.1.0",4 "private": true,5 "type": "module",6 "scripts": {7 "dev": "vite --host 127.0.0.1",8 "build": "vite build",9 "preview": "vite preview --host 127.0.0.1"10 },11 "dependencies": {12 "three": "^0.171.0",13 "vite": "^6.0.7"14 },15 "devDependencies": {}16}17