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>Spooky manor - Halloween</title> </head> <body> <!-- Start your code here --> <div class="manor-app"> <canvas id="manorCanvas"></canvas> <div class="manor-heading"> <span>THE FORGOTTEN ESTATE</span> <h1>BLACKWOOD MANOR</h1> <p>Some things are better left undisturbed.</p> </div> <div class="manor-controls"> <button id="stormButton">⚡ Summon Storm</button> <button id="ghostButton">☠ Reveal Ghost</button> <button id="lightButton">◉ Extinguish Lights</button> </div> </div> <!-- End your code here --> </body> </html>
* { box-sizing: border-box; } body { margin: 0; background: #05080d; font-family: Georgia, serif; } .manor-app { position: relative; width: 100%; height: min(780px, 100vh); min-height: 480px; overflow: hidden; background: #060b13; } #manorCanvas { display: block; width: 100%; height: 100%; } .manor-heading { position: absolute; top: 25px; left: 0; width: 100%; text-align: center; pointer-events: none; color: #e2d8c8; text-shadow: 0 3px 15px #000; } .manor-heading span { font: 10px Arial, sans-serif; letter-spacing: 5px; color: #c7a67a; } .manor-heading h1 { margin: 10px 0; font-size: clamp(26px, 5vw, 58px); font-weight: normal; letter-spacing: 5px; } .manor-heading p { margin: 0; font-size: 13px; font-style: italic; color: #b3bac1; letter-spacing: 1px; } .manor-controls { position: absolute; bottom: 20px; left: 0; width: 100%; display: flex; justify-content: center; flex-wrap: wrap; gap: 10px; padding: 0 15px; } .manor-controls button { background: #10151bdc; color: #dec8a7; border: 1px solid #806a4e; border-radius: 4px; padding: 12px 17px; font: 12px Georgia, serif; cursor: pointer; transition: background .2s; } .manor-controls button:hover { background: #4a3022; } @media (max-width: 520px) { .manor-heading { top: 18px; } .manor-heading h1 { letter-spacing: 2px; } .manor-controls button { font-size: 11px; padding: 10px; } }
const canvas = document.getElementById("manorCanvas"); const ctx = canvas.getContext("2d"); const stormButton = document.getElementById("stormButton"); const ghostButton = document.getElementById("ghostButton"); const lightButton = document.getElementById("lightButton"); let W = 0; let H = 0; let dpr = 1; let time = 0; let lastTime = 0; let storm = false; let ghostVisible = false; let lightsOn = true; let lightning = 0; let stormEnd = 0; const rand = function(n) { const v = Math.sin(n * 127.1 + 78.233) * 43758.5453; return v - Math.floor(v); }; function resize() { const r = canvas.getBoundingClientRect(); W = r.width; H = r.height; dpr = Math.min(window.devicePixelRatio || 1, 2); canvas.width = Math.round(W * dpr); canvas.height = Math.round(H * dpr); ctx.setTransform(dpr, 0, 0, dpr, 0, 0); } window.addEventListener("resize", resize); resize(); function polygon(points, fill, stroke, sw) { ctx.beginPath(); points.forEach(function(p, i) { if (i === 0) ctx.moveTo(p[0], p[1]); else ctx.lineTo(p[0], p[1]); }); ctx.closePath(); if (fill) { ctx.fillStyle = fill; ctx.fill(); } if (stroke) { ctx.strokeStyle = stroke; ctx.lineWidth = sw || 1; ctx.stroke(); } } function rect(x, y, w, h, fill) { ctx.fillStyle = fill; ctx.fillRect(x, y, w, h); } function line(x1, y1, x2, y2, color, width) { ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.strokeStyle = color; ctx.lineWidth = width || 1; ctx.stroke(); } function ellipse(x, y, rx, ry, fill) { ctx.beginPath(); ctx.ellipse(x, y, rx, ry, 0, 0, Math.PI * 2); ctx.fillStyle = fill; ctx.fill(); } function gradient(x, y, r, stops) { const g = ctx.createRadialGradient(x, y, 0, x, y, r); stops.forEach(function(s) { g.addColorStop(s[0], s[1]); }); return g; } function sky() { const g = ctx.createLinearGradient(0, 0, 0, 720); g.addColorStop(0, "#050a15"); g.addColorStop(.55, "#17283c"); g.addColorStop(1, "#050a0c"); rect(0, 0, 1100, 720, g); // Moonlight behind the estate rect(0, 0, 1100, 720, gradient(220, 135, 400, [ [0, "rgba(100,145,195,.23)"], [1, "rgba(0,0,0,0)"] ]) ); // Stars for (let i = 0; i < 120; i++) { const x = rand(i + 5) * 1100; const y = rand(i + 900) * 380; const a = .15 + rand(i + 300) * .5; ellipse(x, y, 1, 1, "rgba(215,225,240," + a + ")"); } // Moon ctx.save(); ctx.shadowColor = "#b7d8ff"; ctx.shadowBlur = 55; ellipse(230, 145, 75, 75, "#d8e0e1"); ctx.restore(); ellipse(230, 145, 75, 75, gradient(210, 115, 120, [ [0, "#f0eee3"], [.7, "#c1ccd4"], [1, "#718597"] ]) ); // Craters for (let i = 0; i < 18; i++) { const a = rand(i + 45) * Math.PI * 2; const rr = rand(i + 88) * 55; ellipse( 230 + Math.cos(a) * rr, 145 + Math.sin(a) * rr, 3 + rand(i + 8) * 8, 3 + rand(i + 12) * 7, "rgba(100,120,140,.13)" ); } } function clouds() { for (let i = 0; i < 12; i++) { const speed = 3 + i * .4; const x = ((i * 170 + time * speed) % 1500) - 250; const y = 60 + rand(i + 50) * 240; ctx.save(); ctx.globalAlpha = .12 + rand(i) * .15; for (let j = 0; j < 5; j++) { ellipse( x + j * 42, y + Math.sin(j) * 12, 85, 30, gradient(x + j * 42, y, 90, [ [0, "#8191a6"], [1, "rgba(20,30,45,0)"] ]) ); } ctx.restore(); } } function hills() { polygon([ [0, 480], [110, 425], [220, 460], [340, 405], [500, 455], [650, 420], [800, 460], [950, 410], [1100, 450], [1100, 720], [0, 720] ], "#091217"); polygon([ [0, 540], [170, 495], [350, 530], [550, 485], [800, 535], [1100, 490], [1100, 720], [0, 720] ], "#071012"); } function stoneWall(x, y, w, h, seed) { const g = ctx.createLinearGradient(x, y, x + w, y); g.addColorStop(0, "#14191d"); g.addColorStop(.35, "#394044"); g.addColorStop(.7, "#262d31"); g.addColorStop(1, "#0b1115"); rect(x, y, w, h, g); // Mortar joints for (let yy = y + 9; yy < y + h; yy += 13) { line(x, yy, x + w, yy, "#101619", 1); const offset = (Math.floor(yy / 13) % 2) * 18; for (let xx = x + offset; xx < x + w; xx += 36) { line(xx, yy - 12, xx, yy, "#11191d", 1); } } // Weathered stone texture ctx.save(); ctx.beginPath(); ctx.rect(x, y, w, h); ctx.clip(); for (let i = 0; i < w * h / 45; i++) { const px = x + rand(seed + i * 2) * w; const py = y + rand(seed + i * 2 + 1) * h; const a = .06 + rand(seed + i + 400) * .13; rect(px, py, 1.5, 2, "rgba(180,190,185," + a + ")"); } ctx.restore(); line(x, y, x, y + h, "#586166", 2); line(x + w, y, x + w, y + h, "#080e12", 3); } function roof(x, y, w, h) { polygon([ [x - 18, y + h], [x + w / 2, y], [x + w + 18, y + h] ], "#101820", "#43505a", 2); // Roof tiles ctx.save(); ctx.beginPath(); ctx.moveTo(x - 18, y + h); ctx.lineTo(x + w / 2, y); ctx.lineTo(x + w + 18, y + h); ctx.clip(); for (let yy = y + 12; yy < y + h; yy += 12) { line(x - 20, yy, x + w + 20, yy, "#35404a", 1); for (let xx = x; xx < x + w; xx += 22) { line(xx + (yy % 24), yy, xx + (yy % 24) - 4, yy + 11, "#28333c", 1); } } ctx.restore(); line(x - 20, y + h, x + w + 20, y + h, "#59646a", 5); } function gothicWindow(x, y, w, h, seed) { const lit = lightsOn && Math.sin(time * 2 + seed) > -.85; ctx.save(); ctx.beginPath(); ctx.moveTo(x, y + h); ctx.lineTo(x, y + w / 2); ctx.quadraticCurveTo(x, y, x + w / 2, y); ctx.quadraticCurveTo(x + w, y, x + w, y + w / 2); ctx.lineTo(x + w, y + h); ctx.closePath(); ctx.fillStyle = "#050708"; ctx.fill(); ctx.save(); ctx.clip(); if (lit) { const flicker = .82 + Math.sin(time * 7 + seed) * .12; ctx.globalAlpha = flicker; rect(x + 4, y + 4, w - 8, h - 8, gradient(x + w / 2, y + h * .6, h, [ [0, "#ffe49a"], [.35, "#e18b32"], [1, "#5d250d"] ]) ); ctx.globalAlpha = 1; } else { rect(x, y, w, h, "#080e14"); } // Curtains and shadows polygon([ [x, y], [x + w * .3, y], [x + w * .2, y + h], [x, y + h] ], "rgba(12,10,13,.7)"); polygon([ [x + w, y], [x + w * .7, y], [x + w * .8, y + h], [x + w, y + h] ], "rgba(12,10,13,.7)"); ctx.restore(); ctx.strokeStyle = "#657077"; ctx.lineWidth = 4; ctx.stroke(); line(x + w / 2, y + 2, x + w / 2, y + h, "#101519", 3); line(x, y + h * .5, x + w, y + h * .5, "#101519", 3); // Decorative stone lintel line(x - 5, y + h + 3, x + w + 5, y + h + 3, "#657077", 5); ctx.restore(); // Light bloom if (lit) { ctx.save(); ctx.globalCompositeOperation = "screen"; rect(x - 28, y - 25, w + 56, h + 50, gradient(x + w / 2, y + h / 2, h, [ [0, "rgba(255,135,30,.16)"], [1, "rgba(255,100,0,0)"] ]) ); ctx.restore(); } } function spire(x, y, w, h) { polygon([ [x, y + h], [x + w / 2, y], [x + w, y + h] ], "#0b141c", "#46545f", 2); line(x + w / 2, y - 25, x + w / 2, y, "#68767b", 3); ellipse(x + w / 2, y - 27, 4, 4, "#6a7474"); } function manor() { // Left wing stoneWall(280, 360, 220, 250, 10); roof(275, 280, 230, 95); // Right wing stoneWall(740, 350, 210, 260, 30); roof(735, 270, 220, 95); // Main central structure stoneWall(445, 285, 355, 325, 60); roof(435, 165, 375, 130); // Central Gothic gable stoneWall(550, 215, 140, 155, 100); roof(545, 115, 150, 110); // Left tower stoneWall(350, 210, 110, 400, 200); spire(330, 75, 150, 145); // Right tower stoneWall(830, 230, 90, 380, 300); spire(815, 95, 120, 145); // Chimneys stoneWall(480, 165, 28, 105, 400); stoneWall(750, 165, 30, 110, 500); // Windows in wings gothicWindow(310, 410, 42, 90, 1); gothicWindow(390, 410, 42, 90, 2); gothicWindow(780, 405, 42, 95, 3); gothicWindow(875, 405, 35, 95, 4); // Central windows gothicWindow(485, 340, 48, 105, 5); gothicWindow(710, 340, 48, 105, 6); gothicWindow(580, 245, 75, 115, 7); // Tower windows gothicWindow(375, 270, 55, 105, 8); gothicWindow(850, 285, 48, 105, 9); // Lower windows gothicWindow(485, 485, 45, 95, 10); gothicWindow(715, 485, 45, 95, 11); // Decorative balcony rect(545, 375, 155, 9, "#596269"); for (let x = 550; x < 700; x += 13) { line(x, 382, x, 405, "#141a1c", 4); } rect(545, 405, 155, 7, "#303b40"); // Main entrance arch ctx.beginPath(); ctx.moveTo(574, 610); ctx.lineTo(574, 520); ctx.quadraticCurveTo(574, 462, 625, 462); ctx.quadraticCurveTo(676, 462, 676, 520); ctx.lineTo(676, 610); ctx.closePath(); ctx.fillStyle = "#080909"; ctx.fill(); ctx.strokeStyle = "#647078"; ctx.lineWidth = 12; ctx.stroke(); // Wooden doors rect(585, 515, 80, 95, "#27170e"); rect(587, 520, 36, 87, "#422516"); rect(627, 520, 36, 87, "#301a11"); line(625, 515, 625, 610, "#090a09", 3); ellipse(616, 570, 3, 3, "#d3a45e"); ellipse(635, 570, 3, 3, "#d3a45e"); // Steps for (let i = 0; i < 5; i++) { rect(565 - i * 8, 610 + i * 8, 120 + i * 16, 9, i % 2 ? "#283034" : "#40494b"); } // Stone balustrade for (let x = 455; x <= 790; x += 17) { if (x > 555 && x < 695) continue; rect(x, 440, 6, 25, "#687078"); } rect(450, 435, 345, 7, "#434e53"); } function ghost() { if (!ghostVisible) return; const x = 615 + Math.sin(time * .8) * 7; const y = 295 + Math.sin(time * 2) * 6; ctx.save(); ctx.globalAlpha = .55 + Math.sin(time * 2) * .2; ctx.shadowColor = "#b2ffff"; ctx.shadowBlur = 20; ellipse(x, y, 15, 20, "#d9f5ed"); polygon([ [x - 15, y], [x + 15, y], [x + 22, y + 65], [x + 8, y + 53], [x, y + 70], [x - 9, y + 54], [x - 20, y + 65] ], "#b6ddd5"); ellipse(x - 6, y - 2, 3, 5, "#0c2228"); ellipse(x + 6, y - 2, 3, 5, "#0c2228"); ctx.restore(); } function tree(x, y, scale, seed) { ctx.save(); ctx.translate(x, y); ctx.scale(scale, scale); function branch(x1, y1, len, angle, depth, n) { if (depth === 0) return; const x2 = x1 + Math.cos(angle) * len; const y2 = y1 + Math.sin(angle) * len; line(x1, y1, x2, y2, depth > 3 ? "#030606" : "#080b0c", depth * 3.2); branch(x2, y2, len * .75, angle - .35 - rand(n) * .4, depth - 1, n + 1); branch(x2, y2, len * .7, angle + .3 + rand(n + 5) * .4, depth - 1, n + 3); } branch(0, 0, 120, -Math.PI / 2, 7, seed); ctx.restore(); } function gate() { // Stone gateposts stoneWall(110, 470, 52, 200, 800); stoneWall(945, 470, 52, 200, 900); polygon([ [100, 470], [136, 440], [172, 470] ], "#333b3b"); polygon([ [935, 470], [971, 440], [1007, 470] ], "#333b3b"); // Iron gate panels for (let side = 0; side < 2; side++) { const x0 = side === 0 ? 165 : 755; for (let i = 0; i < 13; i++) { const x = x0 + i * 14; const top = 500 + Math.sin(i / 12 * Math.PI) * 30; line(x, top, x, 680, "#080c0e", 4); polygon([ [x - 4, top], [x, top - 14], [x + 4, top] ], "#101719"); } line(x0, 560, x0 + 170, 560, "#11181b", 6); line(x0, 635, x0 + 170, 635, "#11181b", 6); } // Lanterns lantern(136, 455); lantern(971, 455); } function lantern(x, y) { rect(x - 13, y - 20, 26, 32, "#0b0c0b"); ctx.save(); ctx.shadowColor = "#ff9b37"; ctx.shadowBlur = 30; rect(x - 8, y - 16, 16, 24, lightsOn ? "#f5a64b" : "#34302a"); ctx.restore(); polygon([ [x - 18, y - 20], [x, y - 38], [x + 18, y - 20] ], "#111719"); line(x, y - 38, x, y - 45, "#151a1b", 2); } function path() { polygon([ [575, 645], [675, 645], [900, 720], [330, 720] ], "#171c1e"); // Cobblestones for (let i = 0; i < 180; i++) { const yy = 650 + rand(i + 1200) * 70; const spread = (yy - 645) * 3.7; const xx = 625 + (rand(i + 1400) - .5) * spread; ellipse(xx, yy, 3 + rand(i) * 9, 1 + rand(i + 4) * 3, "rgba(115,130,140,.22)"); } // Reflected lantern light ellipse(440, 703, 45, 6, "rgba(255,130,35,.13)"); ellipse(800, 698, 55, 7, "rgba(255,130,35,.12)"); } function bats() { ctx.strokeStyle = "#020407"; ctx.lineWidth = 2.5; for (let i = 0; i < 12; i++) { const x = ((i * 95 + time * (18 + i)) % 1350) - 100; const y = 110 + rand(i + 30) * 210 + Math.sin(time * 4 + i) * 9; const s = .5 + rand(i + 8); ctx.beginPath(); ctx.moveTo(x - 13 * s, y); ctx.quadraticCurveTo(x - 8 * s, y - 10 * s, x, y); ctx.quadraticCurveTo(x + 8 * s, y - 10 * s, x + 13 * s, y); ctx.stroke(); } } function fog() { for (let i = 0; i < 12; i++) { const x = ((i * 150 + time * (5 + i)) % 1400) - 150; const y = 580 + rand(i + 70) * 140; rect(x - 150, y - 45, 300, 90, gradient(x, y, 160, [ [0, "rgba(150,170,185,.11)"], [1, "rgba(150,170,185,0)"] ]) ); } } function drawLightning() { if (lightning <= 0) return; ctx.save(); ctx.fillStyle = "rgba(185,215,255," + (lightning * .28) + ")"; ctx.fillRect(0, 0, 1100, 720); ctx.shadowColor = "#d8eaff"; ctx.shadowBlur = 20; ctx.strokeStyle = "#eaf5ff"; ctx.lineWidth = 3; ctx.beginPath(); ctx.moveTo(900, 0); ctx.lineTo(870, 70); ctx.lineTo(895, 110); ctx.lineTo(850, 170); ctx.lineTo(860, 210); ctx.lineTo(825, 260); ctx.stroke(); ctx.restore(); } function vignette() { rect(0, 0, 1100, 720, gradient(550, 350, 800, [ [0, "rgba(0,0,0,0)"], [.7, "rgba(0,0,0,.08)"], [1, "rgba(0,0,0,.8)"] ]) ); } function render(now) { const dt = lastTime ? Math.min((now - lastTime) / 1000, .05) : 0; lastTime = now; time += dt; ctx.clearRect(0, 0, W, H); // Fixed virtual coordinate system ctx.save(); const scale = Math.max(W / 1100, H / 720); const ox = (W - 1100 * scale) / 2; const oy = (H - 720 * scale) / 2; ctx.translate(ox, oy); ctx.scale(scale, scale); sky(); clouds(); hills(); manor(); ghost(); path(); tree(85, 650, 1.4, 1); tree(1030, 660, 1.2, 50); gate(); bats(); fog(); drawLightning(); vignette(); ctx.restore(); lightning = Math.max(0, lightning - dt * 3); if (storm && time > stormEnd) { storm = false; stormButton.textContent = "⚡ Summon Storm"; } if (storm && Math.random() < dt * 1.5) { lightning = 1; } requestAnimationFrame(render); } // Controls stormButton.addEventListener("click", function() { storm = true; stormEnd = time + 9; lightning = 1; stormButton.textContent = "⚡ Storm Active"; }); ghostButton.addEventListener("click", function() { ghostVisible = !ghostVisible; ghostButton.textContent = ghostVisible ? "☠ Hide Ghost" : "☠ Reveal Ghost"; }); lightButton.addEventListener("click", function() { lightsOn = !lightsOn; lightButton.textContent = lightsOn ? "◉ Extinguish Lights" : "◉ Illuminate Manor"; }); requestAnimationFrame(render);
terminal
JavaScript Console
Preview
Clear
close
›
Run