Liveweave: Generative AI Web Editor & HTML/CSS/JS Playground
Initializing
Liveweave
Web
expand_more
home
Home
Palette
Color Explorer
arrow_outward
Polyline
Graphics Editor
arrow_outward
frame_source
Python Playground
arrow_outward
build
Tools
expand_more
restart_alt
Load "Hello Weaver!"
post_add
Generate Lorem ipsum...
code
Format HTML
code_blocks
Format CSS
data_object
Format JavaScript
library_add
Library
expand_more
A
Algolia JS
Animate CSS
Apex Charts JS
B
Bulma CSS
Bootstrap
C
Chart JS
Chartist
Create JS
D
D3
Dojo
F
Foundation
Fullpage JS
G
Granim JS
Google Charts
H
Halfmoon
J
jQuery
M
Materialize
Moment JS
Masonry JS
Milligram CSS
P
Pure CSS
Primer CSS
Popper JS
Pattern CSS
Picnic CSS
R
React JS
Raphael JS
Raisin CSS
S
Semantic UI
Skeleton CSS
Spectre CSS
Tachyons CSS
T
Tailwind
Three JS
U
UI Kit
Vis JS
W
Water CSS
download
Download
expand_more
developer_mode
Download as HTML
folder_zip
Download as .ZIP
cloud_upload
Fork
account_circle
Login
settings
Settings
expand_more
Editor font
14
px
A
A
Night mode
Line number
Mini map
Word wrap
sync_alt
Reset Settings
smart_display
Run
left_panel_close
AI Assistant
dashboard
All
HTML
CSS
JS
visibility
Preview
bolt
Live Preview
terminal
JS Console
<!DOCTYPE html> <html> <head> <title>City walkthrough demo using ThreeJS</title> </head> <body> <!-- Start your code here --> <div id="nova-world"> <div id="nova-canvas"></div> <div class="city-header"> <div class="city-logo">N<span>.</span></div> <div class="city-brand"> <strong>NOVA CITY</strong> <small>EXPLORE THE UNEXPECTED</small> </div> <div class="city-location" id="city-location"> CENTRAL DISTRICT </div> </div> <div class="city-crosshair">+</div> <div class="city-controls"> <strong>EXPLORE NOVA CITY</strong> <p>W A S D — Walk</p> <p>MOUSE — Look around</p> <p>SHIFT — Run</p> <p>ESC — Release mouse</p> </div> <div class="city-actions"> <button id="city-day" type="button">DAY / NIGHT</button> <button id="city-start" type="button">START EXPLORING</button> </div> <div id="city-message" class="city-message"> Loading 3D city... </div> </div> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/build/three.min.js"></script> <!-- End your code here --> </body> </html>
* { box-sizing: border-box; } html, body { margin: 0; width: 100%; height: 100%; overflow: hidden; font-family: Arial, Helvetica, sans-serif; } #nova-world { position: relative; width: 100%; height: 100vh; min-height: 350px; background: #b9e2f4; overflow: hidden; } #nova-canvas { position: absolute; inset: 0; } #nova-canvas canvas { display: block; width: 100%; height: 100%; } .city-header { position: absolute; z-index: 5; top: 20px; left: 25px; right: 25px; display: flex; align-items: center; gap: 12px; color: white; pointer-events: none; } .city-logo { display: grid; place-items: center; width: 48px; height: 48px; border-radius: 13px; background: #172c44; font-size: 29px; font-weight: 900; letter-spacing: -3px; box-shadow: 0 5px 20px #0003; } .city-logo span { color: #ff7757; } .city-brand strong { display: block; font-size: 16px; letter-spacing: 2px; } .city-brand small { display: block; font-size: 9px; letter-spacing: 1.7px; margin-top: 5px; } .city-location { margin-left: auto; padding: 13px 17px; border-radius: 30px; background: #fffffff0; color: #344657; font-size: 10px; font-weight: 900; letter-spacing: 1px; box-shadow: 0 4px 15px #0002; } .city-crosshair { position: absolute; z-index: 4; top: 50%; left: 50%; transform: translate(-50%, -50%); color: white; font-size: 24px; text-shadow: 0 2px 5px #0008; pointer-events: none; } .city-controls { position: absolute; z-index: 5; bottom: 24px; left: 24px; padding: 20px; border-radius: 15px; background: #172e45e8; color: white; box-shadow: 0 5px 25px #0002; } .city-controls strong { display: block; color: #ffb59c; font-size: 10px; letter-spacing: 1.5px; margin-bottom: 13px; } .city-controls p { margin: 9px 0; font-size: 11px; color: #dbe7f0; } .city-actions { position: absolute; z-index: 5; right: 24px; bottom: 24px; display: flex; gap: 10px; } .city-actions button { border: none; padding: 16px 20px; border-radius: 11px; font-size: 11px; font-weight: 900; cursor: pointer; box-shadow: 0 5px 20px #0002; } #city-day { background: white; color: #263b4f; } #city-start { background: #ff7757; color: white; } .city-actions button:hover { filter: brightness(1.08); } .city-message { position: absolute; z-index: 10; top: 50%; left: 50%; transform: translate(-50%, -50%); padding: 20px 30px; border-radius: 15px; background: #ffffffee; color: #263b4f; font-size: 14px; font-weight: bold; text-align: center; box-shadow: 0 10px 40px #0002; pointer-events: none; } .city-message.hidden { display: none; } @media (max-width: 650px) { .city-header { top: 12px; left: 12px; right: 12px; } .city-logo { width: 39px; height: 39px; font-size: 23px; } .city-brand strong { font-size: 12px; } .city-brand small { font-size: 8px; } .city-location { font-size: 8px; padding: 10px; } .city-controls { bottom: 12px; left: 12px; padding: 12px; } .city-controls p { font-size: 10px; } .city-actions { bottom: 12px; right: 12px; flex-direction: column; } .city-actions button { padding: 12px; font-size: 10px; } }
(function () { "use strict"; var attempts = 0; function boot() { var host = document.getElementById("nova-canvas"); var status = document.getElementById("city-message"); if (!host || !status) { attempts++; if (attempts < 50) { setTimeout(boot, 200); } return; } if (typeof THREE === "undefined") { attempts++; if (attempts < 50) { status.textContent = "Waiting for Three.js..."; setTimeout(boot, 200); } else { status.textContent = "Three.js could not load. Check the CDN in HTML."; } return; } try { createCity(host, status); } catch (error) { status.textContent = "3D error: " + error.message; console.error(error); } } function createCity(host, status) { status.textContent = "Creating Nova City..."; var scene = new THREE.Scene(); var dayColor = new THREE.Color(0xaed9f1); var nightColor = new THREE.Color(0x14243e); scene.background = dayColor.clone(); scene.fog = new THREE.Fog(0xaed9f1, 140, 390); var camera = new THREE.PerspectiveCamera( 72, 1, 0.1, 550 ); var renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setPixelRatio( Math.min(window.devicePixelRatio || 1, 1.5) ); renderer.outputEncoding = THREE.sRGBEncoding; renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; host.appendChild(renderer.domElement); var hemi = new THREE.HemisphereLight( 0xffffff, 0x8ca2a0, 1.5 ); scene.add(hemi); var sunlight = new THREE.DirectionalLight( 0xfff2dc, 1.5 ); sunlight.position.set(-70, 110, 65); sunlight.castShadow = true; sunlight.shadow.mapSize.set(1024, 1024); sunlight.shadow.camera.left = -140; sunlight.shadow.camera.right = 140; sunlight.shadow.camera.top = 140; sunlight.shadow.camera.bottom = -140; sunlight.shadow.camera.far = 350; scene.add(sunlight); function mat(color) { return new THREE.MeshLambertMaterial({ color: color }); } var m = { grass: mat(0x83b996), road: mat(0x414d59), pavement: mat(0xcbd0cf), white: mat(0xf4f1e9), dark: mat(0x344556), glass: mat(0x81b9d4), tree: mat(0x53966d), trunk: mat(0x80624b), water: mat(0x65bed8), coral: mat(0xf08069), yellow: mat(0xeecb87), blue: mat(0x86b8c9), pink: mat(0xe6b4ba), mint: mat(0xa9cbb4), stone: mat(0xd9ddd9), line: mat(0xf6ebc9), metal: mat(0x708493) }; function box(w, h, d, x, y, z, material, parent) { var mesh = new THREE.Mesh( new THREE.BoxGeometry(w, h, d), material ); mesh.position.set(x, y, z); mesh.castShadow = h > 1; mesh.receiveShadow = true; (parent || scene).add(mesh); return mesh; } function cylinder(r, h, x, y, z, material, parent) { var mesh = new THREE.Mesh( new THREE.CylinderGeometry(r, r, h, 12), material ); mesh.position.set(x, y, z); mesh.castShadow = true; mesh.receiveShadow = true; (parent || scene).add(mesh); return mesh; } function sphere(r, x, y, z, material, parent) { var mesh = new THREE.Mesh( new THREE.SphereGeometry(r, 10, 8), material ); mesh.position.set(x, y, z); mesh.castShadow = true; (parent || scene).add(mesh); return mesh; } var obstacles = []; function obstacle(x, z, w, d) { obstacles.push({ x1: x - w / 2, x2: x + w / 2, z1: z - d / 2, z2: z + d / 2 }); } function walkable(x, z) { if (Math.abs(x) > 165 || Math.abs(z) > 165) { return false; } var radius = 1.1; for (var i = 0; i < obstacles.length; i++) { var o = obstacles[i]; if ( x + radius > o.x1 && x - radius < o.x2 && z + radius > o.z1 && z - radius < o.z2 ) { return false; } } return true; } // GROUND box(380, 1, 380, 0, -0.7, 0, m.grass); // ORGANIZED STREET GRID var streets = [-120, -60, 0, 60, 120]; streets.forEach(function(p) { box(16, 0.12, 350, p, 0.03, 0, m.road); box(350, 0.12, 16, 0, 0.03, p, m.road); for (var v = -165; v <= 165; v += 17) { box(0.25, 0.02, 7, p, 0.11, v, m.line); box(7, 0.02, 0.25, v, 0.11, p, m.line); } }); // SIDEWALKS for (var ix = -2; ix < 2; ix++) { for (var iz = -2; iz < 2; iz++) { box( 44, 0.28, 44, ix * 60 + 30, 0.12, iz * 60 + 30, m.pavement ); } } // CROSSWALKS AT INTERSECTIONS streets.forEach(function(x) { streets.forEach(function(z) { for (var k = -3; k <= 3; k++) { box( 1.3, 0.025, 3.2, x + k * 1.9, 0.12, z + 10.5, m.white ); } }); }); // DOWNTOWN OFFICE BUILDINGS var towerColors = [ 0x8ea8b9, 0xabc1ca, 0x9bafbf, 0x7e9dac ]; function tower(x, z, h, index) { var group = new THREE.Group(); group.position.set(x, 0, z); scene.add(group); var w = 20; var d = 20; box( w, h, d, 0, h / 2, 0, mat(towerColors[index % 4]), group ); for (var y = 5; y < h - 2; y += 5) { box( w + 0.3, 2.8, d + 0.3, 0, y, 0, m.glass, group ); box( w + 0.5, 0.35, d + 0.5, 0, y + 1.5, 0, m.metal, group ); } box( 22, 1, 22, 0, h + 0.5, 0, m.dark, group ); box( 9, 4, 0.4, 0, 2, 10.3, m.glass, group ); obstacle(x, z, w, d); } tower(-90, -90, 76, 0); tower(-30, -90, 95, 1); tower(-90, -30, 65, 2); tower(-30, -30, 84, 3); // COLORFUL SHOPPING DISTRICT var shopMaterials = [ m.coral, m.blue, m.yellow, m.pink, m.mint ]; function shop(x, z, index) { var group = new THREE.Group(); group.position.set(x, 0, z); scene.add(group); var wall = shopMaterials[index % shopMaterials.length]; var awning = index % 2 === 0 ? m.coral : m.dark; box(11, 13, 12, 0, 6.5, 0, wall, group); box(12, 0.8, 13, 0, 13.4, 0, m.white, group); box(8.5, 4.6, 0.3, 0, 3.1, 6.2, m.glass, group); box(2.2, 4.2, 0.4, 0, 2.2, 6.5, m.dark, group); box(12, 0.55, 2.5, 0, 6, 7.2, awning, group); for (var wx = -3.2; wx <= 3.2; wx += 6.4) { box(3.2, 3.2, 0.3, wx, 10, 6.2, m.white, group); box(2.6, 2.6, 0.4, wx, 10, 6.45, m.glass, group); } obstacle(x, z, 11, 12); } var shopIndex = 0; for (var row = 0; row < 2; row++) { for (var col = 0; col < 2; col++) { var sx = 30 + col * 60; var sz = -90 + row * 60; for (var n = 0; n < 3; n++) { shop( sx - 15 + n * 15, sz, shopIndex++ ); } } } // RESIDENTIAL QUARTER function home(x, z, index) { var group = new THREE.Group(); group.position.set(x, 0, z); scene.add(group); var wall = shopMaterials[index % shopMaterials.length]; box(12, 16, 13, 0, 8, 0, wall, group); box(13, 1, 14, 0, 16.5, 0, m.white, group); for (var floor = 0; floor < 3; floor++) { for (var wx = -3; wx <= 3; wx += 6) { var wy = 4 + floor * 4.7; box(3, 3.2, 0.3, wx, wy, 6.7, m.white, group); box(2.4, 2.6, 0.4, wx, wy, 6.9, m.glass, group); } } box(2.8, 4, 0.4, 0, 2, 7, m.dark, group); box(4.5, 0.35, 2, 0, 0.35, 7.5, m.stone, group); obstacle(x, z, 12, 13); } var homeIndex = 0; for (var hr = 0; hr < 2; hr++) { for (var hc = 0; hc < 2; hc++) { var hx = -90 + hc * 60; var hz = 30 + hr * 60; for (var hi = 0; hi < 3; hi++) { home(hx - 15 + hi * 15, hz, homeIndex++); } } } // RIVERSIDE PARK box(45, 0.18, 45, 30, 0.3, 30, m.grass); box(5, 0.04, 44, 30, 0.42, 30, m.pavement); box(44, 0.04, 5, 30, 0.42, 30, m.pavement); function tree(x, z, size) { cylinder( 0.5, size, x, size / 2, z, m.trunk ); sphere( size * 0.55, x, size * 1.2, z, m.tree ); sphere( size * 0.38, x + 1.6, size * 1.45, z, m.tree ); } var parkTrees = [ [15, 15], [20, 44], [43, 17], [45, 44], [18, 36], [39, 42] ]; parkTrees.forEach(function(p, i) { tree(p[0], p[1], 5 + i % 3); }); // POND IN THE NORTHEAST PARK BLOCK var pond = new THREE.Mesh( new THREE.CylinderGeometry(12, 12, 0.12, 40), m.water ); pond.position.set(90, 0.45, 90); scene.add(pond); for (var t = 0; t < 12; t++) { var angle = t / 12 * Math.PI * 2; tree( 90 + Math.cos(angle) * 17, 90 + Math.sin(angle) * 17, 5 + t % 3 ); } // CENTRAL PLAZA FOUNTAIN // Positioned in the center of the intersection. cylinder(4.4, 0.8, 0, 0.5, 0, m.stone); cylinder(3.6, 0.2, 0, 1, 0, m.water); cylinder(0.7, 3.5, 0, 2.8, 0, m.stone); sphere(1.2, 0, 4.8, 0, m.water); obstacle(0, 0, 9, 9); // STREET LIGHTS var lamps = []; function lamp(x, z) { cylinder(0.16, 7, x, 3.5, z, m.dark); box(2, 0.25, 0.5, x + 0.8, 7, z, m.dark); var glow = new THREE.MeshBasicMaterial({ color: 0xfff0c4 }); box(1, 0.15, 0.4, x + 1.2, 6.8, z, glow); var light = new THREE.PointLight( 0xffd8a0, 0, 17 ); light.position.set(x + 1.2, 6.8, z); scene.add(light); lamps.push(light); } for (var lp = -120; lp <= 120; lp += 60) { for (var lv = -90; lv <= 90; lv += 60) { lamp(lp + 10, lv + 10); } } // MOVING CARS var cars = []; var carColors = [ m.coral, m.yellow, m.blue, m.white, m.dark ]; function car(x, z, index, axis) { var group = new THREE.Group(); group.position.set(x, 0, z); if (axis === "x") { group.rotation.y = Math.PI / 2; } var paint = carColors[index % carColors.length]; box(3.3, 1.3, 6.2, 0, 1.2, 0, paint, group); box(2.8, 1.4, 3, 0, 2.5, -0.4, paint, group); box(2.4, 0.7, 0.2, 0, 2.7, 1.2, m.glass, group); for (var side = -1; side <= 1; side += 2) { for (var wz = -2; wz <= 2; wz += 4) { var wheel = new THREE.Mesh( new THREE.CylinderGeometry( 0.65, 0.65, 0.45, 10 ), m.dark ); wheel.rotation.z = Math.PI / 2; wheel.position.set(side * 1.7, 0.7, wz); group.add(wheel); } } scene.add(group); cars.push({ mesh: group, axis: axis, speed: 7 + index % 4 * 2 }); } for (var ci = 0; ci < 12; ci++) { var axis = ci % 2 === 0 ? "x" : "z"; var road = streets[ci % streets.length]; if (axis === "x") { car(-155 + ci * 24, road + 3.5, ci, axis); } else { car(road + 3.5, -155 + ci * 24, ci, axis); } } // FIRST-PERSON CAMERA var player = { x: 0, y: 2.3, z: 19, yaw: 0, pitch: 0 }; camera.rotation.order = "YXZ"; var keys = {}; var startButton = document.getElementById("city-start"); var dayButton = document.getElementById("city-day"); var locationLabel = document.getElementById("city-location"); var isNight = false; document.addEventListener("keydown", function(e) { keys[e.code] = true; if ( e.code === "ArrowUp" || e.code === "ArrowDown" || e.code === "ArrowLeft" || e.code === "ArrowRight" ) { e.preventDefault(); } }); document.addEventListener("keyup", function(e) { keys[e.code] = false; }); window.addEventListener("blur", function() { keys = {}; }); startButton.addEventListener("click", function() { if (renderer.domElement.requestPointerLock) { renderer.domElement.requestPointerLock(); } }); renderer.domElement.addEventListener("click", function() { if ( document.pointerLockElement !== renderer.domElement && renderer.domElement.requestPointerLock ) { renderer.domElement.requestPointerLock(); } }); document.addEventListener("pointerlockchange", function() { var locked = document.pointerLockElement === renderer.domElement; startButton.textContent = locked ? "EXPLORING..." : "START EXPLORING"; }); document.addEventListener("mousemove", function(e) { if (document.pointerLockElement !== renderer.domElement) { return; } player.yaw -= e.movementX * 0.002; player.pitch -= e.movementY * 0.002; player.pitch = Math.max( -1.25, Math.min(1.25, player.pitch) ); }); // DAY AND NIGHT MODE dayButton.addEventListener("click", function() { isNight = !isNight; if (isNight) { scene.background.copy(nightColor); scene.fog.color.copy(nightColor); hemi.intensity = 0.35; sunlight.intensity = 0.15; lamps.forEach(function(light) { light.intensity = 1.2; }); dayButton.textContent = "SWITCH TO DAY"; } else { scene.background.copy(dayColor); scene.fog.color.copy(dayColor); hemi.intensity = 1.5; sunlight.intensity = 1.5; lamps.forEach(function(light) { light.intensity = 0; }); dayButton.textContent = "DAY / NIGHT"; } }); function resize() { var w = Math.max(1, host.clientWidth); var h = Math.max(1, host.clientHeight); camera.aspect = w / h; camera.updateProjectionMatrix(); renderer.setSize(w, h); } window.addEventListener("resize", resize); var clock = new THREE.Clock(); function animate() { requestAnimationFrame(animate); var dt = Math.min(clock.getDelta(), 0.05); var forward = 0; var strafe = 0; if (keys.KeyW || keys.ArrowUp) forward++; if (keys.KeyS || keys.ArrowDown) forward--; if (keys.KeyA || keys.ArrowLeft) strafe--; if (keys.KeyD || keys.ArrowRight) strafe++; var length = Math.hypot(forward, strafe) || 1; forward /= length; strafe /= length; var speed = keys.ShiftLeft || keys.ShiftRight ? 20 : 9; var dx = ( -Math.sin(player.yaw) * forward + Math.cos(player.yaw) * strafe ) * speed * dt; var dz = ( -Math.cos(player.yaw) * forward - Math.sin(player.yaw) * strafe ) * speed * dt; if (walkable(player.x + dx, player.z)) { player.x += dx; } if (walkable(player.x, player.z + dz)) { player.z += dz; } camera.position.set(player.x, player.y, player.z); camera.rotation.y = player.yaw; camera.rotation.x = player.pitch; cars.forEach(function(c) { if (c.axis === "x") { c.mesh.position.x += c.speed * dt; if (c.mesh.position.x > 170) { c.mesh.position.x = -170; } } else { c.mesh.position.z += c.speed * dt; if (c.mesh.position.z > 170) { c.mesh.position.z = -170; } } }); if (player.x < -15 && player.z < -15) { locationLabel.textContent = "DOWNTOWN"; } else if (player.x > 15 && player.z < -15) { locationLabel.textContent = "SHOPPING DISTRICT"; } else if (player.x > 15 && player.z > 15) { locationLabel.textContent = "RIVERSIDE PARK"; } else if (player.x < -15 && player.z > 15) { locationLabel.textContent = "RESIDENTIAL QUARTER"; } else { locationLabel.textContent = "CENTRAL DISTRICT"; } renderer.render(scene, camera); } resize(); camera.position.set(player.x, player.y, player.z); camera.lookAt(0, 2.3, 0); renderer.render(scene, camera); status.classList.add("hidden"); animate(); } boot(); })();
terminal
JavaScript Console
Preview
Clear
close
›
Run