Inside
AERFORM.
A sculptural lighting object shaped by light, gravity, and the space between. The signature technique is physically lit three.js sculpture.
Construct the object
Three.js 0.169.0 is loaded through an import map. A TorusGeometry with 32 radial and 160 tubular segments is deformed along Z by sin(angle × 3) × 0.22. Recomputed normals let the new surface catch the light correctly.
Give the surface weight
MeshPhysicalMaterial combines a terracotta base, roughness 0.37, metalness 0.18, and a restrained clearcoat. Hemisphere lighting fills the shadow; two directional lights create the key and rim. ACES tone mapping controls highlights.
Separate glow and body
A second thin torus is emissive warm white. A narrow cylinder becomes the cable. The object is an invented sculpture, built entirely from geometry; no model files or environment images are fetched.
Compose for two screens
The camera orbits with scroll. On mobile the object moves below the copy and the stage becomes 1100 pixels tall. Reduced motion stops automatic rotation; finish buttons still update material. A CSS ring is the WebGL fallback.
Reproduce the site
- Download the complete source below and keep its name
index.html. - Open it through a static server. From its directory, run
python3 -m http.server 8000, then visithttp://localhost:8000. There is no install or build step. - The stylesheet lives in the head. The rendering and interaction code lives in the final script. Adjust one parameter at a time and compare the result at desktop and 375 px widths.
- Google Fonts and the pinned Three.js CDN module require a network connection. All visible artwork is computed locally. Native system fallbacks preserve readable text if fonts fail.
- Enable your operating system’s reduced-motion setting and repeat the interaction. Use browser developer tools to check errors, overflow, and frame timing before publishing.
The complete rendering and interaction code
import * as THREE from 'three';const c=document.querySelector('#object'),stage=document.querySelector('.stage'),reduced=matchMedia('(prefers-reduced-motion: reduce)');try{const renderer=new THREE.WebGLRenderer({canvas:c,alpha:true,antialias:true});renderer.setPixelRatio(Math.min(devicePixelRatio,1.5));renderer.setClearColor(0xeae6dc,0);renderer.toneMapping=THREE.ACESFilmicToneMapping;renderer.toneMappingExposure=1.1;const scene=new THREE.Scene(),camera=new THREE.PerspectiveCamera(35,1,.1,50);scene.add(new THREE.HemisphereLight(0xffffff,0x867354,2.8));const key=new THREE.DirectionalLight(0xffe8c9,4.3);key.position.set(3,4,5);scene.add(key);const rim=new THREE.DirectionalLight(0xffffff,2);rim.position.set(-3,1,-4);scene.add(rim);const group=new THREE.Group();scene.add(group);const material=new THREE.MeshPhysicalMaterial({color:0xbf6548,roughness:.37,metalness:.18,clearcoat:.4,clearcoatRoughness:.35});const geo=new THREE.TorusGeometry(1.18,.26,32,160);const pos=geo.attributes.position;for(let i=0;i<pos.count;i++){const x=pos.getX(i),y=pos.getY(i);pos.setZ(i,pos.getZ(i)+.22*Math.sin(Math.atan2(y,x)*3))}geo.computeVertexNormals();const ring=new THREE.Mesh(geo,material);ring.rotation.x=.65;group.add(ring);const glow=new THREE.Mesh(new THREE.TorusGeometry(.94,.025,12,120),new THREE.MeshStandardMaterial({color:0xffe3ae,emissive:0xffca79,emissiveIntensity:3}));glow.rotation.x=.65;group.add(glow);const cable=new THREE.Mesh(new THREE.CylinderGeometry(.007,.007,4,8),new THREE.MeshStandardMaterial({color:0x58514b,roughness:.7}));cable.position.y=3;group.add(cable);function size(){const w=stage.clientWidth,h=stage.clientHeight;renderer.setSize(w,h,false);camera.aspect=w/h;camera.updateProjectionMatrix();group.scale.setScalar(w<650?.45:1);cable.visible=w>=650;group.position.set(w<650?0:1.25,w<650?-1.2:0,0)}size();addEventListener('resize',()=>{size();render(0)});function render(t){const scroll=Math.min(1,scrollY/innerHeight);camera.position.set(Math.sin(scroll*.7)*.8,scroll*.8,7);camera.lookAt(0,0,0);ring.rotation.z=reduced.matches?-.2:Math.sin(t*.0002)*.18-.2;renderer.render(scene,camera)}function loop(t){render(t);if(!reduced.matches)requestAnimationFrame(loop)}loop(0);reduced.addEventListener('change',()=>loop(0));addEventListener('scroll',()=>{if(reduced.matches)render(0)},{passive:true});document.querySelectorAll('[data-finish]').forEach(b=>b.onclick=()=>{document.querySelectorAll('[data-finish]').forEach(x=>x.setAttribute('aria-pressed',String(x===b)));material.color.set(b.dataset.finish==='clay'?0xbf6548:0xe7e3d7);render(0)})}catch(e){document.querySelector('#fallback').style.display='block';document.querySelectorAll('[data-finish]').forEach(b=>b.onclick=()=>{document.querySelectorAll('[data-finish]').forEach(x=>x.setAttribute('aria-pressed',String(x===b)));document.querySelector('#fallback').style.borderColor=b.dataset.finish==='clay'?'#ca684c':'#f3f0e8'})}The complete visual system
:root{--body:'Questrial',sans-serif}body{font-family:var(--body);background:#eae6dc;color:#2c2a27}header{display:flex;justify-content:space-between;padding:30px 5vw;font-size:13px;position:absolute;z-index:3;width:100%}header b{letter-spacing:.18em;font-weight:400}.stage{position:relative;min-height:100svh;display:grid;grid-template-columns:1fr 1fr;align-items:center;padding:10vh 5vw 6vh;gap:40px}#object{position:absolute;inset:0;width:100%;height:100%;z-index:0}.copy{position:relative;z-index:1;max-width:480px;pointer-events:none}.copy h1{font:400 clamp(70px,8vw,130px)/.9 'Italiana';letter-spacing:-.045em;margin:20px 0 28px}.copy p{max-width:30ch;font-size:17px}.copy small{letter-spacing:.14em;font-size:12px}.spec{position:absolute;right:5vw;bottom:35px;z-index:2;display:flex;gap:25px;font-size:12px}.options{display:flex;gap:12px;pointer-events:auto;margin-top:30px}.options button{border:1px solid #79766d;background:transparent;padding:12px 18px;border-radius:50px;font-size:12px}.options button[aria-pressed=true]{background:#2c2a27;color:#eae6dc}.material{padding:90px 7vw;background:#cc603c;display:grid;grid-template-columns:1fr 1fr;gap:10vw}.material h2{font:400 65px/.95 'Italiana';margin:0}.material p{font-size:18px;max-width:42ch}.colophon{background:#eae6dc}#fallback{position:absolute;right:10%;width:33%;aspect-ratio:1;border:50px solid #ca684c;border-radius:50%;box-shadow:inset 10px 15px 20px #75311c77,20px 30px 45px #0003;display:none}@media(max-width:650px){.stage{min-height:1100px;display:block;padding-top:100px}.copy{max-width:300px}.copy h1{font-size:90px}.spec{left:5vw;right:auto;bottom:30px}.material{grid-template-columns:1fr;gap:25px;padding:60px 6vw}.material h2{font-size:52px}header{font-size:12px}.options{margin-top:20px}#fallback{top:620px;right:20%;width:60%}}Original fictional design study. No stock photography, model files, image textures, or sample audio.