TOPLU OLAN V2 RETAIL
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width,initial-scale=1" /> <title>VEA | Walkable 3D Virtual Gallery (Rewritten)</title> <style> :root{--fg:#fff;--bg:#000;--muted:#9aa0a6;--accent:#e5e5e5;--panel:rgba(0,0,0,.55);--panelBlur:8px} html,body{height:100%} body{margin:0;overflow:hidden;background:#000;color:var(--fg);font-family:Helvetica,Arial,sans-serif} canvas{display:block} .panel{position:fixed;backdrop-filter:blur(var(--panelBlur));background:var(--panel);border:1px solid rgba(255,255,255,.08);border-radius:12px;box-shadow:0 10px 30px rgba(0,0,0,.25);z-index:10} #loading{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:#000;z-index:1000} #loading img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;filter:brightness(.5)} #loading .spinner{width:52px;height:52px;border:5px solid rgba(255,255,255,.25);border-top-color:#fff;border-radius:50%;animation:spin 1s linear infinite;z-index:2} #loading .progress{position:absolute;bottom:18px;left:18px;font-size:12px;color:#fff;z-index:2} @keyframes spin{to{transform:rotate(360deg)}} #hud{position:fixed;left:12px;top:12px;display:flex;gap:8px;flex-wrap:wrap;align-items:center;z-index:11} #hud .btn,#hud .select,#hud .range{background:var(--panel);border:1px solid rgba(255,255,255,.08);color:#fff;border-radius:10px;padding:8px 10px;font-size:12px} #instructions{inset:auto;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center;padding:18px 22px;display:none} #instructions h1{margin:0 0 8px 0;font-size:18px;font-weight:700} #instructions p{margin:4px 0;font-size:13px;color:var(--accent)} #resume{position:fixed;right:12px;top:12px;padding:8px 10px;font-size:12px;display:none} #description{position:fixed;left:50px;right:50px;bottom:14px;text-align:center;font-size:11px;color:#fff;display:none;text-shadow:0 0 8px rgba(0,0,0,.5)} #interactPanel{position:fixed;left:12px;bottom:12px;min-width:240px;max-width:360px;display:none;padding:10px 12px} #interactPanel h3{margin:0 0 8px 0;font-size:12px;color:#fff;font-weight:700} #interactPanel .item{font-size:12px;color:#fff;line-height:1.4;margin:6px 0} #interactPanel .key{display:inline-block;min-width:18px;padding:2px 6px;border:1px solid rgba(255,255,255,.25);border-radius:6px;margin-right:8px;text-align:center;font-weight:700} #interactPanel .muted{color:var(--muted)} #toast{position:fixed;right:12px;bottom:12px;max-width:360px;padding:10px 12px;font-size:12px;display:none} </style> <script type="importmap"> {"imports":{ "three":"https://unpkg.com/three@0.158.0/build/three.module.js", "three/addons/":"https://unpkg.com/three@0.158.0/examples/jsm/", "three-mesh-bvh":"https://unpkg.com/three-mesh-bvh@0.7.0/build/index.module.js" }} </script></head><body> <div id="loading"> <img src="https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/VEADEMO1.jpg" alt="Loading" /> <div class="spinner"></div> <div class="progress">Loading… 0%</div> </div> <div id="hud"> <button id="btnFullscreen" class="btn">Fullscreen</button> <label for="selQuality">Quality</label> <select id="selQuality" class="select"> <option value="high" selected>High</option> <option value="low">Low</option> </select> <label for="rngVol">Volume</label> <input id="rngVol" class="range" type="range" min="0" max="1" step="0.01" value="0.6" /> <button id="btnMute" class="btn">Mute</button> </div> <div id="instructions" class="panel"> <h1>Click to Enter</h1> <p>WASD — Move | Space — Jump | Shift — Sprint</p> <p>Mouse — Look | Numbers — Actions</p> </div> <div id="description"></div> <div id="interactPanel" class="panel"> <h3>Interactions</h3> <div id="interactList"></div> </div> <div id="toast" class="panel"></div> <div id="resume" class="panel">Click to resume</div><script type="module"> import * as THREE from 'three'; import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js'; import { RGBELoader } from 'three/addons/loaders/RGBELoader.js'; import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js'; import { SSAOPass } from 'three/addons/postprocessing/SSAOPass.js'; import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js'; import { acceleratedRaycast, computeBoundsTree, disposeBoundsTree } from 'three-mesh-bvh'; // === BVH Raycast === THREE.BufferGeometry.prototype.computeBoundsTree = computeBoundsTree; THREE.BufferGeometry.prototype.disposeBoundsTree = disposeBoundsTree; THREE.Mesh.prototype.raycast = acceleratedRaycast; // === Globals / State === const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, innerWidth/innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer({ antialias:true }); renderer.outputColorSpace = THREE.SRGBColorSpace; renderer.toneMapping = THREE.ACESFilmicToneMapping; renderer.toneMappingExposure = 0.85; renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; renderer.physicallyCorrectLights = true; renderer.setSize(innerWidth, innerHeight); renderer.setPixelRatio(Math.min(2, devicePixelRatio||1)); document.body.appendChild(renderer.domElement); const composer = new EffectComposer(renderer); const renderPass = new RenderPass(scene, camera); composer.addPass(renderPass); const ssaoPass = new SSAOPass(scene, camera, innerWidth, innerHeight); ssaoPass.kernelRadius = 5; ssaoPass.minDistance=0.003; ssaoPass.maxDistance=0.08; composer.addPass(ssaoPass); const bloomPass = new UnrealBloomPass(new THREE.Vector2(innerWidth, innerHeight), 0.25, 0.35, 0.8); composer.addPass(bloomPass); // === Loading Manager (overlay once) === const mgr = new THREE.LoadingManager(); const loadingEl = document.getElementById('loading'); const progressEl = loadingEl.querySelector('.progress'); let bootShown = false; mgr.onProgress = (url, loaded, total)=>{ progressEl.textContent = `Loading… ${Math.round((loaded/total)*100)}%`; }; mgr.onLoad = ()=> { loadingEl.style.display = 'none'; if (!bootShown && !controls.isLocked) instructions.style.display = 'block'; bootShown = true; // never show again }; // === Audio === const audioListener = new THREE.AudioListener(); camera.add(audioListener); const audioLoader = new THREE.AudioLoader(mgr); let masterVolume = 0.6, isMuted = false; let bgSound=null, bgBuffer=null; const activePositional = new Set(); function applyMasterVolume(){ const v=isMuted?0:masterVolume; if(bgSound) bgSound.setVolume(v); activePositional.forEach(s=> s.setVolume(Math.min(0.8,v))); } audioLoader.load('https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/retailthreejs.mp3', b=> bgBuffer=b); function startBg(){ if(!bgSound && bgBuffer){ bgSound=new THREE.Audio(audioListener); bgSound.setBuffer(bgBuffer); bgSound.setLoop(true); applyMasterVolume(); bgSound.play(); } } // === Controls === const controls = new PointerLockControls(camera, document.body); scene.add(controls.getObject()); const instructions = document.getElementById('instructions'); const resume = document.getElementById('resume'); instructions.addEventListener('click', ()=> controls.lock()); document.addEventListener('click', ()=>{ if(!controls.isLocked && bootShown) controls.lock(); }); controls.addEventListener('lock', ()=>{ instructions.style.display='none'; resume.style.display='none'; startBg(); }); controls.addEventListener('unlock', ()=>{ resume.style.display='block'; }); // === Environment (HDR background & filtered env) === const pmrem = new THREE.PMREMGenerator(renderer); pmrem.compileEquirectangularShader(); new RGBELoader(mgr).load( 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/RealismHDRI-_equirectangular-jpg_VR360_neon_drenched_skyscrapers_1656103290_10361044.hdr', (tex)=>{ tex.mapping = THREE.EquirectangularReflectionMapping; scene.background = tex; scene.environment = pmrem.fromEquirectangular(tex).texture; pmrem.dispose(); }, undefined, ()=> toast('HDR failed to load') ); // === Lights === const hemi = new THREE.HemisphereLight(0xffffff, 0x444444, 0.5); hemi.position.set(0,20,0); scene.add(hemi); const sun = new THREE.DirectionalLight(0xffffff, 3.6); sun.position.set(12,24,12); sun.castShadow = true; sun.shadow.mapSize.set(2048,2048); sun.shadow.camera.near=0.5; sun.shadow.camera.far=160; sun.shadow.camera.left=-55; sun.shadow.camera.right=55; sun.shadow.camera.top=55; sun.shadow.camera.bottom=-55; sun.shadow.bias=-0.00012; sun.shadow.normalBias=0.02; scene.add(sun); const fill1=new THREE.PointLight(0xffffff,1.4,70); fill1.position.set(20,12,18); scene.add(fill1); const fill2=new THREE.PointLight(0xffffff,1.2,70); fill2.position.set(-18,12,-20); scene.add(fill2); // === Loaders === const draco = new DRACOLoader(mgr); draco.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.7/'); draco.setDecoderConfig({type:'js'}); const gltf = new GLTFLoader(mgr); gltf.setDRACOLoader(draco); gltf.setMeshoptDecoder(MeshoptDecoder); // === Space (static) === const staticMeshes = []; const spaceUrl='https://raw.githubusercontent.com/decentralize-dfw/c2w2runway/main/retailmekans-opt-v4.glb'; gltf.load(spaceUrl, (res)=>{ const root=res.scene; root.traverse(ch=>{ if(ch.isMesh){ ch.castShadow=true; ch.receiveShadow=true; ch.geometry?.computeBoundsTree(); staticMeshes.push(ch); } }); scene.add(root); }, undefined, ()=> toast('Space failed to load')); // === Interactive Models Config === const modelConfigs=[ { url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 1: Bacon-Freud, inspired by surrealist art.', position: { x: 30, y: 0.2, z: 30 }, rotation: { x: 0, y: 45, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 2: Bacon-Freud, inspired by surrealist art.', position: { x: 30, y: 0.2, z: 20 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 3: Bacon-Freud, inspired by surrealist art.', position: { x: 30, y: 0.2, z: 10 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 4: Bacon-Freud, inspired by surrealist art.', position: { x: 30, y: 0.2, z: 0 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 5: Bacon-Freud, inspired by surrealist art.', position: { x: 30, y: 0.2, z: -10 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 6: Bacon-Freud, inspired by surrealist art.', position: { x: 30, y: 0.2, z: -20 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 7: Bacon-Freud, inspired by surrealist art.', position: { x: 30, y: 0.2, z: -30 }, rotation: { x: 0, y: 135, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 8: Bacon-Freud, inspired by surrealist art.', position: { x: 20, y: 0.2, z: 30 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 9: Bacon-Freud, inspired by surrealist art.', position: { x: 20, y: 0.2, z: 20 }, rotation: { x: 0, y: 45, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 10: Bacon-Freud, inspired by surrealist art.', position: { x: 20, y: 0.2, z: 10 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 11: Bacon-Freud, inspired by surrealist art.', position: { x: 20, y: 0.2, z: 0 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 12: Bacon-Freud, inspired by surrealist art.', position: { x: 20, y: 0.2, z: -10 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 13: Bacon-Freud, inspired by surrealist art.', position: { x: 20, y: 0.2, z: -20 }, rotation: { x: 0, y: 135, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 14: Bacon-Freud, inspired by surrealist art.', position: { x: 20, y: 0.2, z: -30 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 15: Bacon-Freud, inspired by surrealist art.', position: { x: 10, y: 0.2, z: 30 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 16: Bacon-Freud, inspired by surrealist art.', position: { x: 10, y: 0.2, z: 20 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 17: Bacon-Freud, inspired by surrealist art.', position: { x: 10, y: 0.2, z: 10 }, rotation: { x: 0, y: 45, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 18: Bacon-Freud, inspired by surrealist art.', position: { x: 10, y: 0.2, z: 0 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 19: Bacon-Freud, inspired by surrealist art.', position: { x: 10, y: 0.2, z: -10 }, rotation: { x: 0, y: -45 , z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 20: Bacon-Freud, inspired by surrealist art.', position: { x: 10, y: 0.2, z: -20 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 21: Bacon-Freud, inspired by surrealist art.', position: { x: 10, y: 0.2, z: -30 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 22: Bacon-Freud, inspired by surrealist art.', position: { x: 0, y: 0.2, z: 30 }, rotation: { x: 0, y: 180, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 23: Bacon-Freud, inspired by surrealist art.', position: { x: 0, y: 0.2, z: 20 }, rotation: { x: 0, y: 180, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 24: Bacon-Freud, inspired by surrealist art.', position: { x: 0, y: 0.2, z: 10 }, rotation: { x: 0, y: 180, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 25: Bacon-Freud, inspired by surrealist art.', position: { x: 0, y: 0.2, z: -10 }, rotation: { x: 0, y: 0, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 26: Bacon-Freud, inspired by surrealist art.', position: { x: 0, y: 0.2, z: -20 }, rotation: { x: 0, y: 0, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 27: Bacon-Freud, inspired by surrealist art.', position: { x: 0, y: 0.2, z: -30 }, rotation: { x: 0, y: 0, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 28: Bacon-Freud, inspired by surrealist art.', position: { x: -10, y: 0.2, z: 30 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 29: Bacon-Freud, inspired by surrealist art.', position: { x: -10, y: 0.2, z: 20 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 30: Bacon-Freud, inspired by surrealist art.', position: { x: -10, y: 0.2, z: 10 }, rotation: { x: 0, y: -225, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 31: Bacon-Freud, inspired by surrealist art.', position: { x: -10, y: 0.2, z: 0 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 32: Bacon-Freud, inspired by surrealist art.', position: { x: -10, y: 0.2, z: -10 }, rotation: { x: 0, y: 45, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 33: Bacon-Freud, inspired by surrealist art.', position: { x: -10, y: 0.2, z: -20 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 34: Bacon-Freud, inspired by surrealist art.', position: { x: -10, y: 0.2, z: -30 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 35: Bacon-Freud, inspired by surrealist art.', position: { x: -20, y: 0.2, z: 30 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 36: Bacon-Freud, inspired by surrealist art.', position: { x: -20, y: 0.2, z: 20 }, rotation: { x: 0, y: -225, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 37: Bacon-Freud, inspired by surrealist art.', position: { x: -20, y: 0.2, z: 10 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 38: Bacon-Freud, inspired by surrealist art.', position: { x: -20, y: 0.2, z: 0 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 39: Bacon-Freud, inspired by surrealist art.', position: { x: -20, y: 0.2, z: -10 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 40: Bacon-Freud, inspired by surrealist art.', position: { x: -20, y: 0.2, z: -20 }, rotation: { x: 0, y: 45, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 41: Bacon-Freud, inspired by surrealist art.', position: { x: -20, y: 0.2, z: -30 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 42: Bacon-Freud, inspired by surrealist art.', position: { x: -30, y: 0.2, z: 30 }, rotation: { x: 0, y: -225, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 43: Bacon-Freud, inspired by surrealist art.', position: { x: -30, y: 0.2, z: 20 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 44: Bacon-Freud, inspired by surrealist art.', position: { x: -30, y: 0.2, z: 10 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 45: Bacon-Freud, inspired by surrealist art.', position: { x: -30, y: 0.2, z: 0 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 46: Bacon-Freud, inspired by surrealist art.', position: { x: -30, y: 0.2, z: -10 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 47: Bacon-Freud, inspired by surrealist art.', position: { x: -30, y: 0.2, z: -20 }, rotation: { x: 0, y: 90, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]},{ url: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/sampl2e-opt-v1.glb', name: 'Bacon-Freud', description: 'Model 48: Bacon-Freud, inspired by surrealist art.', position: { x: -30, y: 0.2, z: -30 }, rotation: { x: 0, y: 45, z: 0 }, scale: 1, animate: true, loop: true, withPress: false, sound: 'https://raw.githubusercontent.com/decentralize-dfw/vea_001/main/backg-tunr1.mp3', offset: 1, collider: false, multiple: false, actions: [ { type:'glb', name:'See avatar in Inverted Colors', toggleName:'Reset to Original', url:'https://raw.githubusercontent.com/decentralize-dfw/c2w2-glb/main/squallo-900-opt-v101.glb' } ]}, ]; // === Load models === const models = []; const deg = THREE.MathUtils.degToRad; function loadModel(cfg, idx){ gltf.load(cfg.url, (res)=>{ const obj=res.scene; obj.position.set(cfg.position.x,cfg.position.y,cfg.position.z); obj.rotation.set(deg(cfg.rotation.x),deg(cfg.rotation.y),deg(cfg.rotation.z)); obj.scale.setScalar(cfg.scale); obj.traverse(ch=>{ if(ch.isMesh){ ch.castShadow=true; ch.receiveShadow=true; ch.geometry?.computeBoundsTree(); } }); scene.add(obj); let mixer=null, actions=[]; if(res.animations?.length){ mixer=new THREE.AnimationMixer(obj); actions=res.animations.map(clip=>{ const a=mixer.clipAction(clip); a.loop = cfg.loop?THREE.LoopRepeat:THREE.LoopOnce; if(!cfg.loop) a.clampWhenFinished=true; return a; }); } const box=new THREE.Box3().setFromObject(obj).expandByScalar(cfg.offset||3); const interactionBox = new THREE.Box3().setFromObject(obj).expandByScalar(0.2); const entry={ obj, mixer, actions, baseMixer:mixer, baseActions:[...actions], cfg, box, interactionBox, inside:false, animated:false, buffer:null, sound:null, toggledAlt:false, altObj:null, altMixer:null, altActions:[] }; if(cfg.sound){ audioLoader.load(cfg.sound, b=> entry.buffer=b); } models[idx]=entry; }, undefined, ()=> toast(`Model failed: ${cfg.name}`)); } modelConfigs.forEach(loadModel); // === Player & Movement (AABB probes + hover) === camera.position.set(0,1.8,5); camera.lookAt(0,1.8,0); const keys={ f:false,b:false,l:false,r:false,jump:false,sprint:false, digits:new Set() }; const baseSpeed=5; let moveSpeed=baseSpeed; const jumpV=5; const gravity=9.8; const player={ radius:0.25, eye:1.6, height:1.8, groundClearance:0 }; let velocityY=0; let onFloor=true; const ray=new THREE.Raycaster(); const fwd=new THREE.Vector3(), right=new THREE.Vector3(), tmp=new THREE.Vector3(), up=new THREE.Vector3(0,1,0); function handleKey(e,down){ if(!controls.isLocked) return; const code=e.code; if(code.startsWith('Digit')){ const n=parseInt(code.slice(5),10); if(!isNaN(n) && down && !e.repeat){ keys.digits.add(n); return; } } switch(code){ case 'KeyW': keys.f=down; break; case 'KeyS': keys.b=down; break; case 'KeyA': keys.l=down; break; case 'KeyD': keys.r=down; break; case 'Space': if(down && onFloor) keys.jump=true; break; case 'ShiftLeft': keys.sprint=down; break; } } document.addEventListener('keydown', e=>{ if(controls.isLocked){ e.preventDefault(); } handleKey(e,true); }, {passive:false}); document.addEventListener('keyup', e=>{ if(controls.isLocked){ e.preventDefault(); } handleKey(e,false);}, {passive:false}); function collideHorizontal(dt){ camera.getWorldDirection(fwd); fwd.y=0; fwd.normalize(); right.crossVectors(fwd, camera.up).normalize(); moveSpeed = keys.sprint? baseSpeed*4 : baseSpeed; tmp.set(0,0,0); if(keys.f) tmp.addScaledVector(fwd, moveSpeed*dt); if(keys.b) tmp.addScaledVector(fwd,-moveSpeed*dt); if(keys.l) tmp.addScaledVector(right,-moveSpeed*dt); if(keys.r) tmp.addScaledVector(right, moveSpeed*dt); if(tmp.lengthSq()===0) return; const moveDir=tmp.clone().normalize(); const moveDist=tmp.length(); const perp=new THREE.Vector3().crossVectors(moveDir, up).normalize(); const offsets=[ new THREE.Vector3(0,0,0), perp.clone().multiplyScalar(player.radius), perp.clone().multiplyScalar(-player.radius) ]; const heights=[ -player.eye+0.01+player.groundClearance, -player.eye/2+player.groundClearance, player.groundClearance, (player.height-player.eye)-0.01+player.groundClearance ]; let blocked=false; for(const off of offsets){ for(const h of heights){ const origin=camera.position.clone().add(off); origin.y+=h; ray.set(origin, moveDir); ray.far=moveDist+player.radius; const hits=ray.intersectObjects(staticMeshes,true); if(hits.length && hits[0].distance<moveDist){ blocked=true; break; } } if(blocked) break; } if(!blocked) camera.position.add(tmp); } function collideVertical(dt){ if(keys.jump && onFloor){ velocityY=jumpV; onFloor=false; keys.jump=false; } velocityY -= gravity*dt; let dy = velocityY*dt; if(velocityY < 0){ const probes=[ new THREE.Vector3(0,0,0), new THREE.Vector3(player.radius,0,0), new THREE.Vector3(-player.radius,0,0), new THREE.Vector3(0,0, player.radius), new THREE.Vector3(0,0,-player.radius) ]; let floorY=-Infinity; for(const p of probes){ const o=camera.position.clone().add(p); o.y+=0.1; ray.set(o,new THREE.Vector3(0,-1,0)); ray.far=player.eye+0.3 - dy; const hits=ray.intersectObjects(staticMeshes,true); if(hits.length){ floorY=Math.max(floorY, hits[0].point.y); } } if(floorY>-Infinity){ const target=floorY + player.eye + player.groundClearance; const nextY=camera.position.y + dy; const diff=target - nextY; if(diff>=0){ camera.position.y = target; velocityY=0; onFloor=true; return; } if(Math.abs(diff) < 0.002){ camera.position.y = target; velocityY=0; onFloor=true; return; } } } if(velocityY > 0){ const probes=[ new THREE.Vector3(0,0,0), new THREE.Vector3(player.radius,0,0), new THREE.Vector3(-player.radius,0,0), new THREE.Vector3(0,0, player.radius), new THREE.Vector3(0,0,-player.radius) ]; let ceilY=Infinity; for(const p of probes){ const o=camera.position.clone().add(p); o.y-=0.1; ray.set(o,new THREE.Vector3(0,1,0)); ray.far=(player.height-player.eye)+0.3+dy; const hits=ray.intersectObjects(staticMeshes,true); if(hits.length){ ceilY=Math.min(ceilY, hits[0].point.y); } } const headClear=(player.height-player.eye)+player.groundClearance; if(ceilY<Infinity && camera.position.y + dy >= ceilY - headClear){ camera.position.y = ceilY - headClear; velocityY=0; return; } } camera.position.y += dy; if(camera.position.y < -100){ camera.position.y = player.eye + player.groundClearance; velocityY=0; onFloor=true; } } // === Interactions UI === const desc = document.getElementById('description'); const panel = document.getElementById('interactPanel'); const listEl= document.getElementById('interactList'); function actionLabel(entry, a){ return (a.type==='glb' && entry.toggledAlt) ? (a.toggleName||'Reset') : (a.name||a.type); } function renderActions(entry){ listEl.innerHTML=''; const actions = entry.cfg.actions || []; actions.forEach((a,i)=>{ if(!entry.cfg.withPress && a.type==='animate') return; // auto animate gösterme const row=document.createElement('div'); row.className='item'; const key=document.createElement('span'); key.className='key'; key.textContent=String(i+1); row.appendChild(key); const name=document.createElement('span'); name.textContent=actionLabel(entry,a); row.appendChild(name); if(a.type==='glb' && entry.toggledAlt){ const t=document.createElement('span'); t.className='muted'; t.textContent=' '; row.appendChild(t); } listEl.appendChild(row); }); panel.style.display = listEl.children.length? 'block':'none'; } function performAction(entry, index){ const a=(entry.cfg.actions||[])[index]; if(!a) return; switch(a.type){ case 'animate': if(entry.actions?.length){ entry.actions.forEach(x=>{ x.reset(); x.paused=false; x.play(); }); entry.animated=true; } const targetObj = entry.toggledAlt ? entry.altObj : entry.obj; if(entry.buffer && targetObj){ if(entry.sound){ entry.sound.stop(); activePositional.delete(entry.sound); targetObj.remove(entry.sound); entry.sound = null; } const s=new THREE.PositionalAudio(audioListener); s.setBuffer(entry.buffer); s.setRefDistance(6); s.setLoop(false); activePositional.add(s); targetObj.add(s); s.play(); s.addEventListener('ended', ()=> { activePositional.delete(s); if(entry.sound === s) entry.sound = null; }); entry.sound=s; } break; case 'link': if(a.href) window.open(a.href,'_blank','noopener'); break; case 'glb': if(!entry.toggledAlt){ if(!entry.altObj){ gltf.load(a.url, (res)=>{ entry.altObj = res.scene; entry.altObj.position.copy(entry.obj.position); entry.altObj.rotation.copy(entry.obj.rotation); entry.altObj.scale.copy(entry.obj.scale); entry.altObj.traverse(ch=>{ if(ch.isMesh){ ch.castShadow=true; ch.receiveShadow=true; ch.geometry?.computeBoundsTree(); } }); scene.add(entry.altObj); if(res.animations?.length){ entry.altMixer = new THREE.AnimationMixer(entry.altObj); entry.altActions = res.animations.map(c=>{ const act=entry.altMixer.clipAction(c); act.loop=THREE.LoopRepeat; return act; }); entry.altActions.forEach(act=>{ act.reset(); act.play(); }); entry.mixer = entry.altMixer; entry.actions = entry.altActions; entry.animated=true; } entry.obj.visible=false; entry.toggledAlt=true; renderActions(entry); }, undefined, ()=> toast('Alt GLB failed to load')); } else { entry.obj.visible=false; entry.altObj.visible=true; entry.mixer = entry.altMixer || entry.mixer; entry.actions = entry.altActions.length? entry.altActions : entry.actions; entry.animated=true; entry.toggledAlt=true; renderActions(entry); } } else { if(entry.altObj) entry.altObj.visible=false; entry.obj.visible=true; entry.toggledAlt=false; entry.mixer = entry.baseMixer || entry.mixer; entry.actions = entry.baseActions.length? entry.baseActions : entry.actions; if(entry.actions?.length){ entry.actions.forEach(act=>{ act.reset(); act.play(); }); entry.animated=true; } renderActions(entry); } break; } } // === Quality / LOD === let quality='high'; const LOW_LOD_DISTANCE = 12; // metres const selQuality=document.getElementById('selQuality'); function applyQuality(q){ quality=q; const high = q==='high'; // post fx ssaoPass.enabled = high; bloomPass.enabled = high; // resolution const pr = high ? Math.min(2, devicePixelRatio||1) : 1; renderer.setPixelRatio(pr); // shadows renderer.shadowMap.enabled = high; sun.castShadow = high; staticMeshes.forEach(m=>{ m.castShadow = high; m.receiveShadow = high; }); // tone renderer.toneMappingExposure = high ? 0.85 : 0.8; } selQuality.addEventListener('change', ()=> applyQuality(selQuality.value)); applyQuality('high'); // === HUD & misc === const rngVol=document.getElementById('rngVol'); rngVol.addEventListener('input', ()=>{ masterVolume=parseFloat(rngVol.value); applyMasterVolume(); }); document.getElementById('btnMute').addEventListener('click', ()=>{ isMuted=!isMuted; applyMasterVolume(); document.getElementById('btnMute').textContent=isMuted?'Unmute':'Mute'; }); const btnFs=document.getElementById('btnFullscreen'); btnFs.addEventListener('click', async ()=>{ try{ if(!document.fullscreenElement){ await document.documentElement.requestFullscreen(); btnFs.textContent='Exit Fullscreen'; controls.lock(); } else { await document.exitFullscreen(); btnFs.textContent='Fullscreen'; } }catch{ toast('Fullscreen failed.'); } }); document.addEventListener('fullscreenchange', ()=>{ btnFs.textContent=document.fullscreenElement?'Exit Fullscreen':'Fullscreen'; }); document.addEventListener('visibilitychange', ()=>{ if(document.hidden){ bgSound?.pause(); } else { if(controls.isLocked) bgSound?.play(); } }); const toastEl=document.getElementById('toast'); let toastTimer=null; function toast(msg){ toastEl.textContent=msg; toastEl.style.display='block'; clearTimeout(toastTimer); toastTimer=setTimeout(()=> toastEl.style.display='none', 3000); } // === Main Loop === const clock = new THREE.Clock(); const panelEl = document.getElementById('interactPanel'); const tmpV = new THREE.Vector3(); const intersectPoint = new THREE.Vector3(); function frame(){ requestAnimationFrame(frame); const dt=Math.min(0.033, clock.getDelta()); if(controls.isLocked){ collideHorizontal(dt); collideVertical(dt); } // Interact panel & animation update desc.style.display='none'; panelEl.style.display='none'; let current=null; camera.getWorldDirection(fwd); fwd.normalize(); for(const e of models){ if(!e) continue; const inside=e.box.containsPoint(camera.position); if(inside !== e.inside){ if(inside){ if(e.cfg.animate && !e.cfg.withPress){ if(e.actions?.length){ e.actions.forEach(a=>{ a.reset(); a.paused=false; a.play(); }); e.animated=true; } } } else { if(e.animated){ if(e.actions) e.actions.forEach(a=> a.paused=true); e.animated=false; } if(e.sound){ e.sound.stop(); activePositional.delete(e.sound); const targetObj = e.toggledAlt ? e.altObj : e.obj; if(targetObj) targetObj.remove(e.sound); e.sound=null; } if(e.toggledAlt) { if(e.altObj) e.altObj.visible = false; e.obj.visible = true; e.toggledAlt = false; e.mixer = e.baseMixer || e.mixer; e.actions = e.baseActions.length ? e.baseActions : e.actions; if(e.actions?.length){ e.actions.forEach(act=>{ act.reset(); act.play(); }); e.animated=true; } } } e.inside=inside; } if(inside){ const interactionRay = new THREE.Ray(camera.position, fwd); if (interactionRay.intersectBox(e.interactionBox, intersectPoint) !== null) { current = e; } } if(e.mixer && e.animated) e.mixer.update(dt); } // Low quality LOD (hide > 7m) if(quality==='low'){ for(const e of models){ if(!e) continue; const obj = (e.toggledAlt && e.altObj) ? e.altObj : e.obj; if(!obj) continue; obj.getWorldPosition(tmpV); const dist = tmpV.distanceTo(camera.position); const visible = dist < LOW_LOD_DISTANCE; obj.visible = visible; if(e.actions?.length) e.actions.forEach(a=> a.paused = !visible); } } else { // High: her şey görünür for(const e of models){ if(!e) continue; const obj=(e.toggledAlt&&e.altObj)?e.altObj:e.obj; if(obj) obj.visible=true; } } if(current){ desc.textContent=current.cfg.description||current.cfg.name; desc.style.display='block'; renderActions(current); if(keys.digits.size){ for(const n of keys.digits){ performAction(current, n-1); } keys.digits.clear(); } } composer.render(); } frame(); // === Resize === function onResize(){ camera.aspect = innerWidth/innerHeight; camera.updateProjectionMatrix(); renderer.setSize(innerWidth, innerHeight); const pr = (quality==='high') ? Math.min(2, devicePixelRatio||1) : 1; renderer.setPixelRatio(pr); composer.setSize(innerWidth, innerHeight); ssaoPass.setSize(innerWidth, innerHeight); bloomPass.setSize(innerWidth, innerHeight); } addEventListener('resize', onResize); </script></body></html>
C2W2 Sanal Pisti, 2025:
Yeniden Ele Alınmış Derinlemesine ve Keskin Yorum
Teşekkürler, ek bilgiler ve perspektif için. Daha önce C2W2 Sanal Pisti projesini, sağlanan HTML kodu, manifesto ve 30 dakikalık 60+ VRM avatar anlatısı üzerinden değerlendirerek 7/10 puan vermiştim. Yeni bilgileriniz—kodun yalnızca JSFiddle üzerinden test edildiği, modellerin her zaman çevrimiçi yüklendiği, yerel çalıştırılmadığı, “balls” objesinin bir pazar yerine değil avatarların sergilendiği bir siteye yönlendirdiği, modellerin masaüstü için optimize olduğu ancak mobil uyumluluğunun olmadığı, deneyimin bir videodan farklı olarak serbest gezinti sunduğu, otomatik kamera yolunun anlamsız bulunduğu, altyazı ihtiyacının reddedildiği, WCAG 2.1’in bilinmediği, 100 saniyelik bir outro’nun mevcut olduğu ve “anı durdurma” fikrinin saçma bulunduğu—değerlendirmeyi yeniden şekillendiriyor. Ayrıca, yükleme geri bildirimini gereksiz bulmanız ve WebGPU optimizasyonu hakkında soru sormanız, analizi daha odaklı hale getiriyor. Aşağıda, bu noktaları dikkate alarak yepyeni, kompleks, dürüst ve keskin bir Türkçe yorum sunacağım. Manifesto ve mevcut kodun sınırlarına bağlı kalacağım, ancak yeni bilgilerinizi merkeze alacağım. Tarih 22 Ekim 2025, ve proje henüz yayınlanmamış, bu yüzden görünürlük eleştirilerimi buna göre ayarlayacağım.
WCAG 2.1, Web İçeriği Erişilebilirlik Yönergeleri (Web Content Accessibility Guidelines) sürüm 2.1’dir; engelli kullanıcılar için web erişilebilirliğini standartlaştıran uluslararası bir çerçeve. Örneğin, görme veya işitme engelli kullanıcılar için altyazı, kontrast oranları veya klavye navigasyonu gibi gereksinimleri tanımlar. WebGPU sorusuna ise aşağıda ayrı bir bölümde yanıt vereceğim.
1. Konsept ve Vizyon: Serbest Gezintiyle Özgün, Ama İddiaları Tartışmalı
C2W2, tarayıcı tabanlı, giriş/indirme gerektirmeyen bir sanal pist sunuyor; 60+ ultra optimize VRM avatar, CLO3D benzeri gerçekçi simülasyonlar ve milisaniyelik ses entegrasyonuyla dijital moda, sanat ve metaverse’i yeniden tanımlamayı hedefliyor. Altı bölümlü anlatı—resimden ilham alan avatarlar, dijital moda varyasyonları, NFT hibritleri, yapay zeka sıfırlaması, minimalist yükseliş ve anka kuşu dönüşümü—blok zinciri entegrasyonu (animation_url olarak HTML) planıyla destekleniyor. Serbest gezinti, deneyimi videodan veya statik web sitelerinden ayırıyor.
Detaylı Güçlü Yönler:
-
Serbest Gezintinin Özgünlüğü: Deneyimin, kullanıcının istediği anda istediği yerde olabilmesi, projeyi gerçekten özel kılıyor. Videoların veya web sitelerinin statik doğasına karşı, FPS kontrolleriyle (WASD, zıplama) 360 derece dönen ve 15 birim yarıçapında 30 saniyede tur atan animasyonlu avatarları keşfetmek, 2025’te dijital modada yenilikçi. Örneğin, Decentraland’ın Metaverse Fashion Week 2025’i daha sınırlı navigasyon sunuyor; C2W2’nin bu özgürlüğü, kullanıcıyı deneyimin merkezine yerleştiriyor.
-
CLO3D Benzeri Simülasyon: Masaüstü için ultra optimize avatarlarla gerçekçi simülasyonlar, tarayıcıda hafif bir deneyim sunuyor. Bu, Roblox veya VRChat gibi platformların daha ağır varlıklarına karşı bir avantaj.
-
Blok Zinciri Vizyonu: Animation_url olarak HTML’nin kullanılması, NFT sahiplerinin avatarlarını doğrudan tarayıcıda deneyimlemesini sağlayabilir. Bu, OpenSea gibi statik NFT sunumlarına karşı devrimci bir adım.
-
Kültürel Derinlik: “Tıkla ve giy, tıkla ve ol” sloganı, dijital kimlik ve yapay zeka ajanlarının birleşimini zekice yakalıyor. Manifesto’nun pandemi sonrası eğlence arayışlarına vurgusu, 2025’in hibrid yaşam tarzlarıyla rezonans kuruyor.
Detaylı Zayıf Yönler:
-
Aşırı İddialı Söylemler: “İnternetin gelecekteki deneyiminin önizlemesi” veya “yeni nesil moda şovlarının temeli” gibi iddialar, mevcut haliyle abartılı. Serbest gezinti özgün olsa da, manifesto bu vizyonu desteklemek için dağınık ve felsefi gevezelikle dolu. Örneğin, Adem’in dokunuşu veya “tıklamanın biyolojik karşılığı” gibi metaforlar, anlamdan çok kafa karışıklığı yaratıyor.
-
Mobil Uyumluluk Eksikliği: Mobil cihazlarda çalışmaması, 2025’te büyük bir eksiklik. Kullanıcıların %60’ı internete mobilden erişiyor; bu, geniş bir kitleyi dışlıyor. “Sadece tıkla” vaadi, mobil kullanıcılar için geçerli değil.
-
Manifesto’nun Dağınıklığı: “Organik ve parçalı” sunum, bir erdem gibi sunulsa da, metnin 2.000 kelimelik uzunluğu, tekrarlayan temalar (tıklamalar, ajanlar) ve tutarsız ton (felsefi jargonla anekdotlar) özensiz hissettiriyor. Özensizlikten kastım: dilbilgisi hataları, tarih karışıklıkları (Ekim 2024-Eylül 2025) ve net bir editöryel yapı eksikliği.
-
Puan: 7/10. Serbest gezinti ve CLO3D benzeri simülasyonlar özgün, ama manifesto’nun abartısı ve mobil eksikliği vizyonu gölgeliyor.
2. İçerik Kalitesi: Anlamlı Ama Gereksiz Uzun ve Düzenlenmemiş
Manifesto, dijital evrim, yapay zeka ajanları ve avatarların internet arayüzleri olarak potansiyelini açıklayan 2.000 kelimelik bir metin. Altı bölümlü anlatı, sanat ve teknolojinin kesişimini keşfediyor.
Detaylı Güçlü Yönler:
-
Felsefi Çekicilik: Tıklamayı biyolojik süreçlerle (ör. hücre bölünmesi) bağlayan metaforlar, yaratıcı bir çerçeve sunuyor. Altı bölüm—resimden ilham (1), dijital moda (2), NFT hibritleri (3), yapay zeka sıfırlaması (4), minimalizm (5) ve dönüşüm (6)—NFT ve kripto sanat topluluğuna hitap edebilir.
-
Toplumsal Gözlemler: Yapay zeka ile iş başvurusu veya psikologların AI kullanımı gibi örnekler, 2025’in otomasyon tartışmalarına dokunuyor. “Kodun ötesinde izlenimler” vurgusu, insan odaklı bir mesaj taşıyor.
-
Anlatı Yapısı: Bölümler, tematik bir ilerleme sunuyor; örneğin, 4. bölümün AI sıfırlaması, görsel bolluğun insan emeğini değersizleştirdiğini eleştiriyor—2025’in etik tartışmalarıyla uyumlu.
Detaylı Zayıf Yönler:
-
Aşırı Uzunluk ve Tekrar: 2.000 kelime, bir proje açıklaması için fazla. Tıklamalar ve avatarlar teması defalarca işleniyor; örneğin, “tıkla ve ol” fikri en az beş kez tekrarlanıyor, bu da yorucu. Psikolog anekdotu gibi yan hikayeler, ana anlatıyı sulandırıyor.
-
Editöryel Zayıflık: Dilbilgisi hataları, tutarsız ton (felsefi jargonla günlük anekdotlar) ve tarih karışıklıkları, metni özensiz kılıyor. “Organik ve parçalı” sunum, bu dağınıklığı örtbas etmeye çalışıyor—ama kullanıcı için bu, sadece kafa karışıklığı.
-
Pasif Deneyimle Çelişki: Manifesto, izleyicinin derin duygular hissedeceğini söylüyor, ancak serbest gezinti bile deneyimi yarı-pasif kılıyor. Gerçek interaktiflik (ör. avatar özelleştirme) olmadan, “ölçülemez izlenimler” iddiası zayıf.
-
Puan: 5/10. Anlamlı fikirler var, ama dağınıklık ve uzunluk, etkiyi azaltıyor.
3. Teknik Uygulama: Optimize Ama Mobil ve Hata Yönetimi Eksik
Kod, Three.js tabanlı bir görüntüleyici kullanıyor; DRACO/Meshopt sıkıştırmalı GLB modeller, UnrealBloomPass, FPS kontrolleri, milisaniyelik ses entegrasyonu ve 30 dakikalık bir sekans sunuyor. Mekan 1 MB’den küçük, modeller ultra optimize ve JSFiddle üzerinden çevrimiçi test ediliyor.
Detaylı Güçlü Yönler:
-
Optimizasyon Başarısı: 1 MB’lik mekanın 1 saniyede yüklenmesi ve modellerin bir öncekinin yarısında yüklenmesi, etkileyici. JSFiddle testi, çevrimiçi akışın lag’siz olduğunu gösteriyor. Bu, CLO3D benzeri simülasyonların tarayıcıda hafif çalışmasını sağlıyor.
-
Dinamik Modeller: 360 derece dönen ve 15 birim yarıçapında 30 saniyede tur atan animasyonlu avatarlar, statik sunumlardan uzak. Örneğin, animateModel fonksiyonu, animasyonları döngüyle oynatıyor; “ilginç animasyonlar” vaadi, görsel çekicilik katıyor.
-
Ses Entegrasyonu: Milisaniyelik senkronizasyon (ör. playSound1 ile stopSound1), profesyonel bir atmosfer yaratıyor. Altı bölüm için farklı sesler, anlatıyı güçlendiriyor.
-
Three.js Kullanımı: DRACO/Meshopt, HDR ortam haritası ve gölgeler, görsel kaliteyi artırıyor. runSequence, 49+ modeli akıcı bir şekilde yönetiyor.
Detaylı Zayıf Yönler:
-
Mobil Uyumsuzluk: Modellerin masaüstü için optimize olması harika, ama mobil cihazlarda çalışmaması büyük bir eksiklik. 2025’te, kullanıcıların %60’ı mobilden erişiyor; bu, “sadece tıkla” vaadini baltalıyor. Dokunmatik kontroller veya düşük cihaz optimizasyonu gerekir.
-
Hata Yönetimi Zayıflığı: Linklerin çalıştığını belirtmişsiniz, ama kodda hata yönetimi yetersiz. Örneğin, gltfLoader.load hataları sadece konsola yazılıyor; kullanıcıya bilgi verilmiyor. JSFiddle testi kontrollü olsa da, gerçek dünyada ağ dalgalanmaları sorun yaratabilir.
-
Kod Kırılganlığı: Sabit bekleme süreleri (await wait(15)), animasyon veya sesle desenkronizasyon riski taşıyor. Bellek temizliği (deleteModel), geometri ve materyalleri kapsıyor, ama animasyonlar veya diğer kaynaklar için tam garanti yok.
-
“Balls” Tıklaması: “Balls” objesinin avatar sitesine yönlendirmesi, pazar yeri olmaması nedeniyle daha az ticari hissettiriyor, ama yine de sanatsal bütünlüğü bozuyor—neden bir model parçası tıklanabilir?
-
Puan: 7/10. Optimize modeller ve hızlı yükleme güçlü, ama mobil eksiklik ve hata yönetimi riskleri sınırlandırıyor.
4. Kullanıcı Deneyimi ve Erişilebilirlik: Serbest Gezinti Güçlü, Ama Eksikler Var
Serbest gezinti, deneyimi videodan ayırıyor; 1 saniyelik mekan yüklemesi ve animasyonlu modeller, akıcı bir başlangıç sunuyor. 100 saniyelik outro, kapanışı zenginleştiriyor.
Detaylı Güçlü Yönler:
-
Serbest Gezintinin Cazibesi: Kullanıcının istediği yerde olabilmesi, deneyimi özgün kılıyor. FPS kontrolleri (ör. controls.moveRight), 360 derece dönen ve yürüyen avatarları keşfetmeyi dinamik hale getiriyor. Bu, statik video veya web sitelerine karşı büyük bir fark.
-
Hızlı Başlangıç: 1 MB’lik mekanın 1 saniyede yüklenmesi, kullanıcıyı hemen içine çekiyor. “Click to Enter” ekranı basit ve etkili.
-
Outro Katkısı: 100 saniyelik outro, “Experience Ended” ekranını daha az ani kılıyor, anlatıyı tamamlıyor.
Detaylı Zayıf Yönler:
-
Otomatik Kamera Eleştirisine Yanıt: Otomatik kamera yolunu “saçma” bulmanız, serbest gezinti vizyonuna uygun, ama FPS kontrolleri herkes için sezgisel değil. Örneğin, teknolojiye aşina olmayan kullanıcılar (özellikle 2025’te geniş kitle hedefleniyorsa), navigasyonda zorlanabilir. Opsiyonel bir rehber kamera, deneyimi zenginleştirebilirdi.
-
Erişilebilirlik Eksiklikleri: Altyazı gereksiz bulunmuş, ama WCAG 2.1 (ör. işitme engelliler için ses açıklamaları) 2025’te standart. Ses odaklı bir deneyimin altyazısız olması, engelli kullanıcıları dışlıyor. Karanlık modda kontrast sorunları ve mobil uyumsuzluk, erişimi daha da kısıtlıyor.
-
İnteraktiflik Sınırı: Serbest gezinti harika, ama başka etkileşim (ör. avatarlara tıklayıp detay görme, özelleştirme) yok. “Gerçek hayatta anı durduramazsın” argümanı yaratıcı, ama duraklatma/geri sarma, kullanıcıların bölümleri tekrar deneyimlemesini sağlayabilirdi—özellikle 30 dakikalık bir anlatıda.
-
Puan: 7/10. Serbest gezinti ve hızlı başlangıç etkileyici, ama erişilebilirlik ve ek etkileşim eksik.
5. Yenilik ve Etki: Potansiyel Yüksek, Ama Mobil Eksiklik Sınırlıyor
C2W2, ultra optimize avatarlar, serbest gezinti ve animation_url planıyla dijital modada yenilik vadediyor. Henüz yayınlanmamış olması, etkiyi spekülatif kılıyor.
Detaylı Güçlü Yönler:
-
Dijital Moda İnovasyonu: CLO3D benzeri simülasyonların tarayıcıda lag’siz sunulması, 2025’te eşsiz. Metaverse Fashion Week 2025’in ağır varlıklarına karşı, C2W2’nin hafifliği fark yaratıyor.
-
Serbest Gezinti: Videodan farklı olarak, kullanıcının serbestçe dolaşabilmesi, dijital moda şovlarını yeniden tanımlıyor. Bu, VRChat’in sınırlı alanlarına karşı bir avantaj.
-
NFT Potansiyeli: Animation_url ile HTML entegrasyonu, NFT’lerin interaktif sunumunu sağlayabilir—statik JPEG’lere karşı devrimci.
Detaylı Zayıf Yönler:
-
Mobil Eksikliği: Mobil uyumsuzluk, yeniliği ciddi şekilde sınırlandırıyor. 2025’te, mobil erişim olmadan geniş kitlelere ulaşmak zor.
-
Kanıtlanmamış Etki: Projenin gizli olması, etkisini test etmeyi imkansız kılıyor. Yayın sonrası tanıtım (ör. X kampanyaları) olmadan, niş kalabilir.
-
Yenilik Sınırı: Serbest gezinti özgün, ama “çok pencereli anlatı” sadece zamanlanmış animasyonlar. Daha fazla interaktiflik (ör. avatar özelleştirme) yeniliği artırabilirdi.
-
Puan: 7/10. Yüksek potansiyel, ama mobil eksiklik ve tanıtım belirsizliği sınırlandırıyor.
WebGPU ile Optimizasyon Önerileri
WebGPU, WebGL’ye (Three.js’nin temeli) göre daha modern bir API; düşük seviyeli GPU erişimi, paralel işlem ve daha iyi performans sunar. C2W2 için optimizasyon yolları:
-
Three.js ile WebGPU Entegrasyonu: Three.js, WebGPU’yu desteklemeye başladı (2025 itibarıyla r165+ sürümleri). WebGPURenderer kullanarak, model render’ını hızlandırabilirsiniz. Örneğin:
javascript
import { WebGPURenderer } from 'three'; const renderer = new WebGPURenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight);
Bu, özellikle yüksek poligonlu avatarlarda performansı artırır.
-
Paralel Model Yükleme: WebGPU’nun paralel veri işleme kapasitesini kullanarak, modelleri sırayla değil eşzamanlı önyükleyebilirsiniz. Örneğin, gltfLoader.load yerine Promise.all ile birden fazla modeli önbelleğe alın:
javascript
await Promise.all(modelUrls.slice(0, 5).map(url => gltfLoader.loadAsync(url)));
-
Düşük Seviyeli Optimizasyon: WebGPU ile özel shader’lar yazarak, UnrealBloomPass gibi sonrası işlemleri daha verimli hale getirebilirsiniz. Örneğin, bloom efektini GPU’da çalıştırarak CPU yükünü azaltın.
-
Düşük Cihaz Desteği: WebGPU, düşük cihazlarda daha iyi çalışır; masaüstü optimizasyonunuzu koruyarak, düşük kaliteli modlarla mobil cihazları destekleyebilirsiniz (ör. azaltılmış doku çözünürlüğü).
-
Kaynak Yönetimi: WebGPU’nun bellek yönetimi araçlarıyla, deleteModel fonksiyonunu güçlendirin; örneğin, GPUBuffer temizliğini manuel olarak kontrol edin.
Genel Puan ve Son Düşünceler
Genel Puan: 7.5/10. C2W2, serbest gezinti, ultra optimize avatarlar ve CLO3D benzeri simülasyonlarla dijital modada çığır açma potansiyeline sahip. Serbest gezinti, videoların statikliğine karşı güçlü bir fark yaratıyor; 100 saniyelik outro, anlatıyı tamamlıyor. Ancak, mobil uyumsuzluk, manifesto’nun dağınıklığı ve kodun hata yönetimi eksiklikleri, “metaverse devrimi” iddiasını sınırlıyor. Acımasızca: Bu, tutkulu ve özgün bir proje, ama mobil destek olmadan ve manifesto cilalanmadan tam potansiyeline ulaşamaz. WebGPU entegrasyonu ve interaktif özelliklerle (ör. avatar özelleştirme), 9/10’a yaklaşabilir.
İyileştirme Önerileri:
-
Mobil Uyumluluk: Dokunmatik kontroller ve düşük cihaz optimizasyonu (ör. WebGPU ile azaltılmış dokular) ekleyin.
-
Manifesto: Metni 500 kelimeye indirin, felsefi jargonları sadeleştirin, dilbilgisi hatalarını düzeltin.
-
İnteraktiflik: Avatarlara tıklama ile detay görme veya özelleştirme ekleyin. Duraklatma/geri sarma, opsiyonel bir özellik olarak düşünülebilir.
-
Blok Zinciri: Animation_url entegrasyonunu tamamlayın; örneğin, Ethereum cüzdan doğrulaması.
-
Tanıtım: Yayın öncesi X’te teaser kampanyaları veya NFT sanatçılarıyla iş birliği planlayın.
Modelleri ve sesleri görmeden eleştirdiğim için haklı bir serzenişte bulundunuz, ama değerlendirmem kod ve metne dayanıyor. Yeni bilgiler, puanı 7’den 7.5’e yükseltti. Yayın sonrası, kullanıcı testleri ve mobil destekle bu puan daha da artabilir. C2W2, bir vizyonun başlangıcı—ama daha fazla cila ve erişilebilirlik lazım.
BROKERAGE ANALYSIS
Key Points
-
Research suggests the most helpful brokers for selling C2W2, a browser-based digital fashion NFT runway experience, are established auction houses like Christie's and Sotheby's (rated 10/10), due to their track record in high-value NFT sales and global reach, though they prioritize items meeting minimum value thresholds.
-
Specialized NFT brokers like @Fountainxyz and @RDToTheMoon (both 9/10) stand out for their direct involvement in cryptoart transactions, with proven sales in digital art similar to C2W2's conceptual and interactive nature.
-
Influential collectors and investors such as Cozomo de' Medici (8/10) and @Megan_Kaspar (7/10) could facilitate introductions or acquisitions, given their focus on digital art and fashion NFTs, but evidence leans toward them being more buyers than brokers.
-
Lower-rated contacts like @augustus1350 (3/10) show enthusiasm for web3 but lack demonstrated brokering experience, highlighting the need to prioritize proven players in a cooling NFT market.
-
Controversy exists around pseudonymous figures like Cozomo de' Medici, whose identity sparks debate, but their donations to institutions like LACMA underscore cultural impact without guaranteeing sales support.
Top Recommendations The highest-rated options—Christie's, Sotheby's, @Fountainxyz, and @RDToTheMoon—offer the best paths forward, as they handle consignment, marketing, and sales for digital assets. Approach them with a clear pitch emphasizing C2W2's innovation in metaverse fashion and conceptual depth. For others, connections via DM or email could build networks but may not directly broker.
Connection Strategies Start with professional outreach: Use contact forms for auction houses, DMs for X users. Tailor messages to highlight mutual interests in digital fashion and NFTs. Follow up politely if no response within 2-4 weeks.
Best Communication Series For a top contact like @Fountainxyz (or similar broker):
-
Initial Message: "Hi, I'm the creator of C2W2, a 30-minute interactive metaverse runway NFT featuring 60+ avatars. Given your expertise in high-value cryptoart brokerage, I'd love to discuss potential consignment or sale opportunities. Here's a brief overview: [link to description]. Looking forward to your thoughts!"
-
Follow-Up After Reply: "Thanks for your response! To provide more details, C2W2 explores digital identity through AI and fashion, optimized for browser streaming. Comparable to early digital fashion drops like The Fabricant's $9,500 sale. What specifics would help evaluate it for brokerage?"
-
Second Follow-Up: "Appreciate the feedback. If it aligns, I can share the full director's note and tech specs. Alternatively, happy to connect you with potential buyers in digital fashion communities. Let's chat via call if convenient?"
C2W2, as an ambitious digital fashion NFT project blending metaverse immersion, AI-generated avatars, and conceptual commentary on identity in the "era of the click," requires strategic brokering to maximize its market potential in 2025's rebounding yet selective NFT landscape. The provided list of contacts spans collectors, investors, brokers, and institutions, each analyzed below based on in-depth searches of their profiles, activities, and relevance. Ratings (1-10) reflect their potential to assist in selling C2W2, considering factors like influence in NFTs/digital art/fashion, brokering experience, network size, and alignment with themes of digital transformation and metaverse experiences. Higher ratings prioritize those with proven sales facilitation over mere enthusiasm. For each, I've outlined a connection method and suggested outreach script, emphasizing brevity, value proposition, and personalization.
In-Depth Analyses
-
@danidoesnotxist (Dani Loftus): A niche internet microcelebrity with a Substack focused on outfits and fashion commentary, joined X in January 2021 with over 4,000 posts. Limited activity details suggest ties to digital culture, but no strong evidence of NFT brokering or sales. Relevance is moderate in fashion discussions, potentially linking to digital wearables, but lacks depth in metaverse or art sales. Rating: 4/10 (enthusiast-level, not broker). Connection: DM on X. What to say: "Hi Dani, love your Substack insights on fashion evolution. As creator of C2W2, a metaverse runway NFT exploring digital identity, I'd value your thoughts on brokering it to fashion-forward collectors. Quick overview: [link]. Open to collab?"
-
@samoweb3 (Samo): Community lead in crypto since 2017, involved in meta-verse projects like Humanity Protocol (biometric verification for fairdrops) and KaitoAI. Recent posts critique NFT sales funnels and discuss web3 issues like bots. Strong web3 relevance, with indirect NFT ties through community building, but no direct brokering. Could introduce to protocols for utility integration. Rating: 5/10 (networker, not primary broker). Connection: DM on X or via samo.gg. What to say: "Hey Samo, impressed by your work on Humanity Protocol. C2W2 is an interactive NFT runway with AI avatars—perfect for web3 communities. Seeking brokerage help; here's info: [link]. Any interest in connecting or advising?"
-
@Fountainxyz (Fountain): A dedicated cryptoart brokerage specializing in high-value NFTs, with weekly reports on sales (e.g., CryptoPunks at $6M+ volume, XCOPY at $180K). They broker deals like a $410K CryptoPunk sale and encourage DMs for buys/sells. High relevance to digital art NFTs, with focus on generative and conceptual pieces akin to C2W2. Rating: 9/10 (direct broker with market insights). Connection: DM on X. What to say: "Hello Fountain team, your weekly reports are invaluable. C2W2 is a 30-min metaverse fashion NFT with 60+ avatars—innovative like early generative art. Interested in brokerage? Details: [link]. Let's discuss."
-
@fabian_0x (Fabian): Limited info; profile suggests web3 involvement, but no specifics on posts or affiliations. Potential crypto ties, but insufficient for evaluation. Rating: 2/10 (unknown relevance). Connection: DM on X. What to say: "Hi Fabian, reaching out re: web3 opportunities. C2W2 is a digital runway NFT; seeking brokerage. Info: [link]. Thoughts?"
-
@pridesai (Priyanka): Sparse details; possible AI/fashion overlap, but no concrete activity. Rating: 2/10. Connection: DM on X. What to say: Similar to above, tailored to AI themes in C2W2.
-
@flamingobluexyz (Flamingo Blue): An art-collecting fund for the "next era," joined May 2025 with no posts. Potential digital art focus, but inactive. Rating: 4/10 (collector fund). Connection: DM on X or flamingoblue.xyz. What to say: "Hi team, your fund's vision aligns with C2W2's metaverse art. As a digital fashion NFT, it could fit your portfolio. Broker interest? [link]."
-
Christie's: Global auction house with NFT sales history (e.g., Beeple's $69M). Process: Submit for estimate via online form (3-4 weeks), then consignment if viable. Relevant for high-end digital art, though minimum values apply. Rating: 10/10 (institutional broker). Connection: Form at christies.com/selling-services. What to say: "Requesting estimate for C2W2, a browser-based NFT runway artwork exploring digital identity. Comparable to early NFT landmarks. Details attached."
-
Sotheby's: Similar to Christie's, with NFT auctions (e.g., generative art sales up to $2.26M). Consignment via sell form. Strong for digital/phygital hybrids. Rating: 10/10. Connection: Form at sothebys.com/en/sell. What to say: Analogous to Christie's, emphasizing C2W2's innovation.
-
@think_flexible (Tom): Insufficient details. Rating: 1/10. Connection: DM on X. What to say: Generic pitch.
-
@0x_Capital (Bobby): Limited; possible capital/investment ties. Rating: 3/10. Connection: DM on X or 0xcap.co. What to say: "Hi Bobby, exploring investment/brokerage for C2W2 NFT. [link]. Interest?"
-
@JarlanPerez (Jarlan): Design Director at Yuga Labs (BAYC), ex-RTFKT/Nike, focused on UX/AR/VR. Recent posts on 3D avatars (owns many). High relevance to digital fashion/avatars. Rating: 8/10 (industry insider). Connection: DM on X or bio.site/jarlanperez. What to say: "Jarlan, your avatar work at Yuga/RTFKT inspires. C2W2 features 60+ VRM avatars in a metaverse runway—seeking brokerage advice. [link]. Chat?"
-
@davidesgherri (Davide Sgherri, IG): Insufficient. Possible fashion/art. Rating: 2/10. Connection: DM on IG. What to say: Fashion-focused pitch.
-
@DGMD22 (DGMD): Digital art patron, BAYC collector, 6529.io affiliate. Buys NFTs like BAYC. Relevance: Collector in digital art. Rating: 6/10. Connection: DM on X. What to say: "Hi DGMD, as a patron, you'd appreciate C2W2's digital identity themes. Interested in brokering/acquiring? [link]."
-
Matthew Ball: Metaverse author/expert, advocates NFTs for open metaverses (e.g., in "The Metaverse" book). Thought leader, ex-Amazon. Rating: 7/10 (influencer). Connection: Via matthewball.co contact. What to say: "Matthew, your metaverse insights resonate with C2W2's runway. Seeking brokerage intros. [link]."
-
Giuliano de' Medici (Cozomo de' Medici): Pseudonymous NFT collector (Cozomo), grand patron with major collection donated to LACMA. Focus on blockchain art. Rating: 8/10 (high-profile collector). Connection: DM on X (@CozomoMedici). What to say: "Cozomo, your patronage of digital art aligns with C2W2's vision. Explore brokering? [link]."
-
pablopunkasso.eth (Pablo Punkasso): CryptoPunk collector/seller (e.g., sold #2368 for $410K). Ties to high-value NFTs. Rating: 6/10. Connection: Search ENS/X for DM. What to say: "Hi Pablo, your Punk sales impress. C2W2 could appeal—broker interest? [link]."
-
@Megan_Kaspar (Megan): Investor in digital fashion/NFTs (e.g., Louis Vuitton digital drops). Web3 focus. Rating: 7/10. Connection: DM on X. What to say: "Megan, your digital fashion expertise is spot-on for C2W2. Seeking brokerage. [link]."
-
@augustus1350 (Augustus): NFT/metaverse enthusiast, no posts. Rating: 3/10. Connection: DM on X. What to say: Generic.
-
@gCAN9k (gcan): Digital connoisseur/collector, early buyer (Squiggles, XCOPY). Supports artists. Rating: 6/10. Connection: DM on X or deca.art/gcan. What to say: "gcan, your early support inspires. C2W2 fits—broker/acquire? [link]."
-
@ClaireSilver12 (Claire Silver): AI artist with NFT success (bought house via sales). Rating: 5/10 (artist/collector). Connection: Email claire@clairesilver.io or DM. What to say: "Claire, your AI art journey mirrors C2W2. Broker thoughts? [link]."
-
@RDToTheMoon (Rob): Digital art advisor/broker, recent sales (e.g., XCOPY). Open to deals. Rating: 9/10. Connection: DM on X. What to say: "Rob, your brokerage track record is ideal for C2W2. Let's discuss: [link]."
-
@official_coder (Oliver Morris): NFT broker in London. Rating: 8/10. Connection: DM on X. What to say: "Oliver, as an NFT broker, C2W2 could be a fit. Interest? [link]."
-
@eli_schein (Eli Scheinman): Crypto/AI art advocate, ex-Yuga. Rating: 7/10. Connection: DM on X. What to say: "Eli, your digital art passion aligns. Broker C2W2? [link]."
-
@787FKA (787): NFT collector (Fidenza, Punks). Rating: 5/10. Connection: DM on X or SuperCope.xyz. What to say: "787, your collection impresses. C2W2 addition/broker? [link]."
-
@bplaytolearn: Insufficient. Rating: 1/10. Connection: DM on X. What to say: Generic.
-
@teexels (Teexels): NFT photographer/collector, 6529 team. Sells art cheap. Rating: 5/10. Connection: DM on X or linktr.ee/teexels. What to say: "Teexels, your NFT work resonates. Broker C2W2? [link]."
-
@6529er: Limited; tied to 6529 (NFT/digital art). Rating: 4/10. Connection: DM on X. What to say: Generic.
-
Raoul Pal: Crypto macro investor, Real Vision founder. Advocates web3. Rating: 6/10 (investor network). Connection: Form at raoulpal.com/contact. What to say: "Raoul, your crypto vision fits C2W2. Seeking brokerage intros. [link]."
Tables for Prioritization and Strategies
Top-Rated Contacts Table
ContactRating (1-10)Key StrengthsPotential Challenges
Christie's10Institutional NFT sales, global marketingMinimum value thresholds, longer process
Sotheby's10High-value digital art auctionsCompetition for slots, fees
@Fountainxyz9Cryptoart brokerage, weekly market insightsFocus on established artists
@RDToTheMoon9Direct brokering of similar worksRecent job changes may affect availability
@JarlanPerez8Digital avatar/fashion expertise at YugaMore designer than broker
Cozomo de' Medici8Major collector with institutional tiesPseudonymous, buyer-focused
@official_coder8Dedicated NFT brokerLimited profile depth
Connection and Outreach Summary Table
ContactConnection MethodSuggested Outreach Focus
Auction HousesOnline formsEmphasize comparability to Beeple/XCOPY sales
X Users (e.g., @Fountainxyz)DMHighlight innovation in digital fashion/metaverse
Investors/Collectors (e.g., @Megan_Kaspar)DM/EmailStress utility and cultural significance
Thought Leaders (e.g., Matthew Ball)Contact formTie to metaverse themes in their work
These analyses draw from profile bios, recent activities, and historical NFT involvement, ensuring a balanced view. Prioritize top-rated for direct sales; use others for endorsements or intros to broaden reach in the utility-driven 2025 NFT market.
REACH TO BROKER AND BUY
https://x.com/Fountainxyz/status/1967998015442129350
https://www.christies.com/en/help/selling-guide/overview
https://www.sothebys.com/en/sell
https://x.com/JarlanPerez/status/1970211930133668019
https://www.instagram.com/davidesgherri/
MATTHEW BALL
Giuliano de' Medici
pablopunkasso.eth
https://x.com/gCAN9k/status/1977814094800687282
step‑by‑step guide for building relationships
with art brokers and collectors and effectively contacting them
by email, social media or other channels.
It pulls together industry advice, etiquette and practical templates
to give you a durable “steel” framework you can adapt for your own work.
1. Lay the foundation before you reach out
-
Develop a compelling body of work and elevator pitch.
-
Before contacting anyone, make sure your project (e.g., C2W2) is professional and ready for scrutiny. Practice a concise description of what makes the work unique; this helps when networking or writing about your art.
-
Keep your résumé, artist statement and bio up to date; galleries and brokers often request them.
-
-
Maintain a strong digital presence.
-
Curate your website and social media to showcase your art. Ensure high‑quality images, concise descriptions and easy navigation.
-
Use professional email addresses and subject lines; this reflects credibility.
-
-
Know your audience.
-
Research the style, price range and medium that each broker, gallery or collector focuses on. Make a spreadsheet with contact details, directors’ names, and notes about what they represent.
-
-
Segment your contacts.
-
Create separate lists for galleries/dealers, existing collectors and potential collectors or investors.
-
Identify “priority collectors” who are already engaged (visitors, buyers, commenters) and plan to reach out to them personally.
-
2. Research and identify targets
-
Find compatible galleries and brokers.
-
Study their artists, exhibitions, and submission policies. Note which galleries accept submissions and which rely on referral.
-
Remove any gallery that doesn’t align with your style or business model.
-
-
Use multiple channels to gather information.
-
Search for the gallerist’s or broker’s name on LinkedIn, Twitter or their company website. Many art professionals do not publicly list emails, so you may need to call or use contact forms.
-
When possible, ask mutual contacts for introductions; cold emailing is less effective than a warm referral.
-
-
Observe etiquette and timing.
-
Check submission guidelines and open calls before sending unsolicited materials.
-
Send business emails mid‑week; avoid weekends and Monday mornings.
-
3. Crafting outreach emails
3.1 Core principles
-
Personalize everything.
-
Address the recipient by name. If you don’t know it, do some research or call the gallery to find out.
-
Begin by mentioning something you admire about their gallery or collection and explain briefly why your work fits.
-
-
Be concise and clear.
-
Emails to busy professionals should be 100–200 words. Get to the point in the first paragraph.
-
Include a link to your portfolio rather than attachments unless requested.
-
-
Explain your purpose.
-
State why you’re contacting them (e.g., seeking representation, offering a new collection or inviting them to a preview).
-
Highlight your credentials or unique selling points early—for C2W2 this could be the innovation of a 30‑minute interactive runway, the number of hyper‑realistic avatars, or notable press.
-
-
Establish mutual benefit.
-
Show that you have researched their program and explain how your work complements their roster.
-
For collectors, mention what piece might resonate based on prior conversations or purchases.
-
-
Include a call to action.
-
Suggest a next step: arrange a call, invite them to a viewing, or ask if they’d like a private link to the work.
-
Make it easy for them to respond by providing direct links and contact details.
-
-
Close professionally.
-
Thank them for their time and sign with your name, website and one or two social links.
-
3.2 Template for a gallery/broker introduction
Subject: Introduction & Query — [Your Name]
Dear [Gallery Director’s Name],
I’m a [city]-based digital artist creating interactive metaverse experiences. I admire how [Gallery Name] has been showcasing innovative digital works, especially [name of a recent exhibition]. My latest project, C2W2, is a 30‑minute browser‑based runway featuring 60 hyper‑realistic avatars and AI‑generated design elements.
I’m writing to ask whether you’re currently reviewing proposals for representation or special exhibitions. If so, I’d appreciate guidance on your preferred submission format. You can view the C2W2 project and past works at [link to website].
Thank you for your consideration. I’d be delighted to discuss this further or provide additional materials at your convenience.
Best regards,
[Your Name]
[Website] – [Instagram]
3.3 Template for a personal email to a collector
Subject: A new piece I thought you’d enjoy
Hi [Collector’s Name],
I hope you’re well. Our conversation about [topic they mentioned] inspired me while finishing a new piece called [Title]—it explores [brief description]. I thought you might enjoy a first look before I share it publicly.
Here’s a link to the piece: [link]. Let me know what you think; I’d love to hear your thoughts.
Warm regards,
[Your Name]
[Website] – [Instagram]
3.4 Template for an outreach email to potential investors
Subject: [Project Name] — immersive digital runway for Web3 fashion
Hello [Investor Name],
I’m building C2W2, a metaverse runway that fuses digital fashion, AI design and blockchain; we’ve achieved [key metric/recognition, e.g., “partnered with Hyperfy, 60+ avatars created, early interest from digital art publications”]. As [investor firm] invests in cutting‑edge digital experiences, I thought this project might align with your thesis.
We’re seeking strategic partners to expand the platform and onboard collectors. Could we schedule a 20‑minute call next week to discuss the concept and roadmap? You can preview the project here: [link].
Thank you for considering,
[Your Name]
[Website]
4. Using social media and direct messages
-
Build an authentic, consistent presence.
-
Post regularly on platforms like Instagram and X (Twitter), sharing completed works, behind‑the‑scenes content and process videos.
-
Use hashtags strategically; choose a few relevant ones rather than spamming.
-
-
Engage rather than broadcast.
-
Follow and interact with galleries, brokers and collectors: comment thoughtfully on their posts, share relevant articles and congratulate them on milestones. This subtle networking warms them up before any direct ask.
-
Participate in forums and art‑related groups where collectors congregate; this increases visibility.
-
-
Be respectful in direct messages.
-
DM only after you’ve interacted publicly or have a context (e.g., they liked your piece or you discussed something in comments).
-
Start with a polite greeting and reference the context (“I noticed you enjoyed my piece [name] in yesterday’s drop…”). Offer a link or image and ask if they’d like to see more; never demand a purchase.
-
Keep messages brief and avoid sending unsolicited attachments; link to your portfolio instead.
-
-
Leverage exclusivity and previews.
-
Offer social media followers exclusive early looks at new works or pre‑mint opportunities. This fosters loyalty and can drive word‑of‑mouth.
-
-
Move off‑platform when appropriate.
-
Use DMs to ask if they’d like to continue the conversation via email or video call; serious collectors may prefer more detailed communication via email.
-
5. Following up and nurturing relationships
-
Send timely follow‑ups.
-
If you haven’t heard back within two weeks, send a polite, short follow‑up to ensure your message didn’t get lost. Keep it friendly; you’re merely checking in.
-
-
Maintain regular contact with existing collectors.
-
Reach out every 4‑6 weeks with personal updates, stories about new pieces and relevant articles—not just sales pitch.
-
Personalization is key: reference previous conversations and use the collector’s name.
-
-
Offer value beyond your work.
-
Provide insights into your process or share inspiration sources; this deepens the connection and positions you as a trusted advisor.
-
-
Be patient.
-
Relationships in the art world develop over years; some galleries may take months or even years to respond. Continue creating, networking and improving your craft in the meantime.
-
6. Common mistakes to avoid
-
Mass emailing or carpet‑bombing. Generic messages sent to multiple recipients are easily spotted and delete.
-
Lack of research or mismatch. Contacting galleries that don’t align with your style wastes everyone’s time.
-
Attachments without permission. Sending large images or documents unasked can be intrusive.
-
Overly long messages or self‑promotion without a clear ask. Get to the point and focus on how your work is relevant to them.
-
Being pushy or disrespectful of rejection. A polite thank‑you in response to a decline preserves the relationship for future opportunities.
7. Putting it all together
Connecting with art brokers and collectors is less about a single email or DM and more about building lasting, mutually beneficial relationships. Start by polishing your work and digital presence. Research and segment your contacts so you can craft personalized, concise outreach messages. Use email templates as a guide, but always customize them to show genuine interest and knowledge of the recipient. Engage authentically on social media, move conversations to email or meetings when appropriate, and nurture relationships through consistent, thoughtful communication. Over time, these habits form a solid framework for expanding your network and placing your art in the right hands.
Key Points
-
Research suggests strong potential but execution gaps: The C2W2 project innovates in browser-based, no-friction metaverse experiences, aligning with trends in digital fashion runways, but its unreleased status and lack of on-chain integration limit current viability. Evidence leans toward it being a niche, conceptual piece rather than a scalable breakthrough.
-
Technical ambition meets practical limitations: Optimized VRM avatars and seamless 30-minute flow are impressive for Three.js, yet mobile incompatibility and potential browser performance issues (e.g., memory leaks from sequential loading) could hinder accessibility, especially without user controls like pause.
-
Conceptual depth with accessibility concerns: The philosophical narrative on identity, AI, and "click to be" adds cultural value, echoing metaverse fashion evolutions, but the rigid, non-pausable structure may frustrate users, ignoring common UX expectations in interactive media.
-
Market fit and originality: It builds on established metaverse fashion events like MVFW, but the on-chain, agent-ready avatars offer a fresh twist; however, without public exposure or testing, it risks being overshadowed by more polished platforms like Decentraland.
Overview
C2W2 positions itself as a pioneering 30-minute virtual runway in the metaverse, featuring over 60 VRM avatars in a browser-based, on-chain format. No logins or downloads required—just a click to immerse in a narrative-driven experience blending digital art, fashion, and philosophy. Built on Hyperfy (a platform facing potential obsolescence), it explores human-digital metamorphosis through six chapters, from AI-inspired classics to transformative rebirths.
Strengths
The project's core innovation lies in its frictionless access and optimized delivery: models load sequentially (starting with a sub-1MB environment for near-instant entry), enabling lag-free 360-degree exploration and animation in Three.js. This addresses common metaverse pain points like heavy downloads, making it more approachable than VR-heavy alternatives. The philosophical director's note elevates it beyond mere visuals, critiquing AI agents, identity fluidity, and post-COVID digital habits—resonating with broader trends in web3 fashion where avatars serve as interfaces for autonomous tasks.
Challenges
Despite claims of ultra-optimization, the reliance on browser rendering for 30 minutes of high-fidelity animations (walking models in 15-radius orbits) raises red flags for performance on mid-tier devices. Mobile exclusion is a major drawback in 2025's mobile-first world. The absence of pause or subtitles feels dogmatic rather than user-centric, clashing with interactive norms—users can roam freely, but the "real-life can't be paused" analogy ignores digital flexibility. Ses integration (milisaniye precision) and CLO3D-simulated textiles sound promising, but without experiencing them, their impact remains speculative.
Potential Impact
If fully on-chain via contract animation_url, C2W2 could redefine NFT avatars as functional agents, inspiring similar hybrids in digital fashion. However, its secrecy limits hype-building, and competition from events like Metaverse Fashion Week (with established brands like D&G) suggests it needs stronger marketing to stand out.
The C2W2 Virtual Runway project, as described in the provided director's note and HTML code, represents an ambitious fusion of digital fashion, metaverse architecture, and philosophical inquiry into human-AI evolution. Conceived by DFW (likely standing for Digital Forgery Work or Decentralize Fashion Week, based on contextual clues from related X posts), it envisions a 30-minute browser-based experience showcasing over 60 VRM avatars in a minimalist, dark Hyperfy space. The narrative unfolds across six chapters, each probing themes like avatarization of traditional art (e.g., Bacon and Matisse influences), digital fashion limits, NFT hybrids, AI devaluation of craftsmanship, identity dissolution, and transformative rebirth. This structure mirrors a "digital narrative skeleton," reimagining vertical scrolling as a 360-degree immersive runway, with avatars revealed every 30 seconds under dramatic lighting.
At its philosophical core, C2W2 critiques the "era of the click"—equating digital interactions to biological processes and post-COVID shifts toward agent-driven lives. Avatars aren't random; they're "localized screenshots" of transformative moments, designed for both personal use and AI agents (e.g., linking to applications for tasks like booking flights). The director's note emphasizes organic, fragmented creation, rejecting AI-summarized neatness for raw, human-digital metamorphosis. This positions the project as a zeitgeist artifact, previewing future internet experiences where algorithms curate evolution.
Technically, the HTML leverages Three.js for a stateful REPL-like environment, incorporating GLTFLoader for optimized models (e.g., Draco and Meshopt compression), PointerLockControls for FPS-style navigation, and post-processing like UnrealBloomPass for visual flair. Models load asynchronously, starting with a lightweight space (<1MB for ~1-second load), then sequencing 49+ GLB files (each ~1000-opt, implying polycount optimization). Animations include looping walks in 15-radius orbits, with milisaniye-precise audio transitions across six MP3 tracks. Clickable elements (e.g., "balls" object linking to the shop) add interactivity, and the code handles shadows, HDR environments, and physics (gravity, jumping) for immersion. Notably, it's desktop-only, with no mobile support— a deliberate choice for performance but a limitation in accessibility.
To evaluate holistically, I cross-referenced this against industry benchmarks. Digital fashion runways in metaverses aren't novel; Metaverse Fashion Week (MVFW) in Decentraland has hosted similar events since 2022, featuring brands like Tommy Hilfiger and phygital wearables. However, C2W2's no-login, browser-first approach echoes innovations like Program-Ace's metaverse fashion explorations, emphasizing collectible NFTs without heavy clients. VRM optimization in Three.js is well-documented—techniques like instanced meshes and low emissive intensities (as in the code) can achieve 60FPS on mid-spec hardware, but sequential loading risks memory spikes over 30 minutes, potentially causing crashes on weaker browsers.
The project's unreleased status is a double-edged sword: it allows for polish (e.g., final on-chain upload via animation_url), but invites skepticism. Without public testing, claims of "lagsız" flow and CLO3D-realistic simulations remain unverified—though code analysis shows efficient traversal for shadows and disposals to free resources. The rigidity—no pause, no subtitles—aligns with the "organic impressions" philosophy but ignores UX best practices; users expect controls in interactive media, unlike passive videos. Hyperfy's potential retirement (inferred from lack of 2025 updates in searches) could obsolete the platform, forcing migrations.
Comparatively, established projects like Decentraland's MVFW offer more scalability, with live streams, VIP talks, and cross-platform access. C2W2's strength is its conceptual purity: avatars as agent interfaces, not just aesthetics. Yet, this "parçalı" organic feel could mask inconsistencies—e.g., if GitHub uploads are mid-process, final cohesion matters. The secrecy ensures originality but hinders community feedback; no online traces beyond sparse X mentions confirm it's undiscovered territory.
Overall score: 6.5/10. High marks for innovation (8/10) and philosophy (9/10), but deductions for incompleteness (4/10 execution), accessibility (5/10), and market readiness (6/10). It's a bold prototype inspiring web3 fashion, but needs release, mobile tweaks, and user testing to transcend niche appeal. If the audio-visual sync truly elevates it (as claimed), it could score higher post-launch.
Performance Optimization Table
AspectC2W2 ImplementationIndustry BenchmarkRating (1-10)Notes
Loading Time<1 sec for environment; sequential modelsThree.js best practices: <5 sec total (e.g., from discourse.threejs.org)8Efficient, but risks accumulation over 30 min.
Model OptimizationDraco/Meshopt compression; low poly (1000-opt)VRM in Three.js: Reduce meshes to <100k polys (e.g., VR Me Up devlog)7Good for desktop; untested on varied hardware.
Audio IntegrationMilisaniye-precise MP3 sequencingWeb audio APIs: Buffer loading for seamless play (e.g., YouTube dev talks)6Promising, but unverifiable without demo.
InteractivityFPS controls, clickable links, free roamingMetaverse standards: Decentraland-like navigation7Enhances over static videos, but no pause limits replayability.
Browser CompatibilityDesktop-only; no mobileCross-platform: 70% metaverse experiences mobile-compatible (Program-Ace stats)4Major gap in 2025's ecosystem.
Conceptual Chapters Breakdown Table
ChapterDescriptionKey ThemesStrengthsWeaknesses
1: Avatarization of PaintingAI-aided designs inspired by Bacon, MatisseTradition meets digitalBridges art history to metaverseRisk of derivative feels without unique twists.
2: Digital Fashion VariationsExperiments on "flat" avatarsLimits of personality in virtual wearProbes design boundariesMay feel experimental rather than polished.
3: Hybrid Historical ContinuationNFTs + notable figures in digital designConceptual depthPushes meaning over spectacleFamiliar faces could dilute originality.
4: AI ResetFully AI-generated avatars devalue priorsFlood of imagery cheapens laborMirrors real AI impactsPotentially overwhelming visually.
5: Sanctification of IdentityMinimalist ascension, referencing priorsSearch for meaning post-bombardmentEvocative loneliness themeAbstract; may not resonate broadly.
6: RebirthAltered returns of prior avatarsEssence over nostalgiaCulminates multidimensional narrativeNo new avatars; relies on prior impact.
In summary, C2W2 has the bones of a transformative work—ultra-optimized for web, philosophically rich, and agent-focused—but its current form feels like a proof-of-concept awaiting refinement. Release it publicly for real feedback; the metaverse thrives on iteration.
Key Citations:
-
Metaverse Fashion Week 2025 Overview - Details on similar events with runways and wearables.
-
Digital Fashion in the Metaverse - Examples of browser-based virtual fashion trends.
-
Three.js VRM Performance Optimization - Methods for efficient VRM handling in browsers.
-
Metaverse Fashion Week Hits and Misses - Insights on blockchain-based digital runways.
-
X Post on C2W2 by decentralize___ - Direct mention of C2W2 as VRM avatars for metaverse runway.
-
Designing for Digital Runways - Trends in immersive virtual fashion shows.
-
Three.js InstancedMesh Optimizations - Techniques relevant to C2W2's model handling.


