Skip to content

Instantly share code, notes, and snippets.

@1337hero
Created July 29, 2026 23:54
Show Gist options
  • Select an option

  • Save 1337hero/0ac2a647a8a4ab19c4a79367784575ef to your computer and use it in GitHub Desktop.

Select an option

Save 1337hero/0ac2a647a8a4ab19c4a79367784575ef to your computer and use it in GitHub Desktop.
Qwen3.6-27B-MTP-Q8-Voxel-Test
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>桜の庭 — Cherry Blossom Pagoda</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{overflow:hidden;background:#000;font-family:'Segoe UI',system-ui,sans-serif}
canvas{display:block}
#ui-overlay{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:10}
#title-card{position:absolute;top:20px;left:20px;background:rgba(0,0,0,0.55);backdrop-filter:blur(12px);border:1px solid rgba(255,255,255,0.08);border-radius:14px;padding:18px 24px;color:#fff;min-width:240px;pointer-events:auto}
#title-card h1{font-size:22px;font-weight:300;letter-spacing:2px;margin-bottom:2px}
#title-card h1 span.jp{font-size:26px;font-weight:400;color:#FFB7C5}
#title-card .subtitle{font-size:11px;color:rgba(255,255,255,0.45);letter-spacing:3px;text-transform:uppercase;margin-top:6px}
#title-card .voxel-count{font-size:11px;color:rgba(255,255,255,0.35);margin-top:10px;font-variant-numeric:tabular-nums}
#controls-panel{position:absolute;bottom:20px;left:20px;background:rgba(0,0,0,0.55);backdrop-filter:blur(12px);border:1px solid rgba(255,255,255,0.08);border-radius:14px;padding:16px 20px;color:#fff;pointer-events:auto}
#controls-panel h3{font-size:10px;text-transform:uppercase;letter-spacing:2px;color:rgba(255,255,255,0.35);margin-bottom:10px}
.time-btns{display:flex;gap:6px;flex-wrap:wrap}
.time-btn{background:rgba(255,255,255,0.08);border:1px solid rgba(255,255,255,0.12);color:#fff;padding:6px 14px;border-radius:8px;cursor:pointer;font-size:12px;transition:all .2s;font-family:inherit}
.time-btn:hover{background:rgba(255,255,255,0.18)}
.time-btn.active{background:rgba(255,183,197,0.25);border-color:rgba(255,183,197,0.4);color:#FFB7C5}
.toggle-row{display:flex;gap:8px;margin-top:10px}
.toggle-btn{background:rgba(255,255,255,0.06);border:1px solid rgba(255,255,255,0.1);color:rgba(255,255,255,0.6);padding:5px 12px;border-radius:7px;cursor:pointer;font-size:11px;transition:all .2s;font-family:inherit}
.toggle-btn:hover{background:rgba(255,255,255,0.14)}
.toggle-btn.on{background:rgba(255,183,197,0.2);color:#FFB7C5;border-color:rgba(255,183,197,0.3)}
#hint-line{position:absolute;bottom:20px;right:20px;background:rgba(0,0,0,0.5);backdrop-filter:blur(12px);border:1px solid rgba(255,255,255,0.06);border-radius:10px;padding:12px 16px;color:rgba(255,255,255,0.35);font-size:11px;line-height:1.8;pointer-events:auto}
#hint-line kbd{background:rgba(255,255,255,0.1);padding:1px 6px;border-radius:4px;font-size:10px;color:rgba(255,255,255,0.5)}
#time-label{position:absolute;top:20px;right:20px;background:rgba(0,0,0,0.5);backdrop-filter:blur(12px);border:1px solid rgba(255,255,255,0.06);border-radius:10px;padding:10px 16px;color:rgba(255,255,255,0.6);font-size:13px;font-weight:300;letter-spacing:1px;pointer-events:auto}
#loading{position:fixed;top:0;left:0;width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;background:#0a0a0f;z-index:100;transition:opacity .5s}
#loading.done{opacity:0;pointer-events:none}
#loading .main{color:#FFB7C5;font-size:48px;font-weight:300}
#loading .sub{font-size:13px;color:rgba(255,255,255,0.3);margin-top:12px;letter-spacing:2px}
#loading .err{color:#ff4444;font-size:14px;max-width:500px;text-align:center;margin-top:12px}
</style>
</head>
<body>
<div id="ui-overlay">
<div id="title-card">
<h1><span class="jp">桜の庭</span> Cherry Blossom Pagoda</h1>
<div class="subtitle">Voxel Garden Diorama</div>
<div class="voxel-count" id="voxel-count">voxels: —</div>
</div>
<div id="controls-panel">
<h3>Time of Day</h3>
<div class="time-btns">
<button class="time-btn" data-time="dawn">🌅 Dawn</button>
<button class="time-btn active" data-time="day">☀️ Day</button>
<button class="time-btn" data-time="dusk">🌇 Dusk</button>
<button class="time-btn" data-time="night">🌙 Night</button>
<button class="time-btn" data-time="auto">🔄 Auto</button>
</div>
<div class="toggle-row">
<button class="toggle-btn on" id="btn-petals">🌸 Petals</button>
<button class="toggle-btn" id="btn-autoorbit">◎ Auto Orbit</button>
<button class="toggle-btn" id="btn-fly">🚁 Cinematic Fly</button>
</div>
</div>
<div id="hint-line">
<kbd>Left Drag</kbd> Orbit <kbd>Scroll</kbd> Zoom <kbd>Right Drag</kbd> Pan<br>
<kbd>1</kbd> Dawn <kbd>2</kbd> Day <kbd>3</kbd> Dusk <kbd>4</kbd> Night
</div>
<div id="time-label">Day — 12:00</div>
</div>
<div id="loading"><div class="main"></div><div class="sub" id="load-status">building voxel garden…</div></div>
<script type="importmap">
{"imports":{"three":"https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js","three/addons/":"https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/"}}
</script>
<script type="module">
import*as THREE from'three';
import{OrbitControls}from'three/addons/controls/OrbitControls.js';
const LS=document.getElementById('load-status');
function setStatus(s){LS.textContent=s;}
// ─── RNG ───
class RNG{constructor(s=42){this.s=s}next(){this.s=(this.s*16807)%2147483647;return this.s/2147483647}range(a,b){return a+this.next()*(b-a)}int(a,b){return Math.floor(this.range(a,b+1))}pick(a){return a[this.int(0,a.length-1)]}chance(p){return this.next()<p}}
const R=new RNG(42);
// ─── VOXEL MAP ───
const VM=new Map();
const vk=(x,y,z)=>`${x},${y},${z}`;
const av=(x,y,z,c)=>{const k=vk(x,y,z);if(!VM.has(k))VM.set(k,{x,y,z,c})};
const hv=(x,y,z)=>VM.has(vk(x,y,z));
// ─── PALETTES ───
const P={
grass:[0x4CAF50,0x388E3C,0x66BB6A,0x81C784,0x558B2F],
darkGrass:[0x2E7D32,0x33691E,0x1B5E20],
earth:[0x8B6914,0x6B4423,0x96703A,0x7B5B3A],
stone:[0x808080,0x757575,0x9E9E9E,0x616161,0xA0A0A0],
darkStone:[0x555555,0x424242,0x616161],
pagRed:[0xC23B22,0xB71C1C,0xD32F2F,0xA8201A],
pagDark:[0x8C1A10,0x7A1509,0x6D1008],
roof:[0x1C2D3A,0x2C3E50,0x172530,0x34495E,0x15202B],
roofDk:[0x0F1A22,0x0A1419,0x1A2730],
gold:[0xD4A017,0xFFD700,0xC5931A,0xF0C700],
win:[0xFFD54F,0xFFCA28,0xFFC107,0xFFB300],
p1:[0xFFB7C5,0xFF91A4,0xFFC0CB],
p2:[0xFF69B4,0xF4A0B5,0xE8899E],
p3:[0xFF85A2,0xFF7096,0xFF5C8A],
p4:[0xFFA0B4,0xFFD1DC,0xFFE0E8],
trunk:[0x5C3317,0x4A2810,0x6B3A20,0x8B4513],
g1:[0x228B22,0x2E8B57],
g2:[0x3CB371,0x2E8B57],
g3:[0x006400,0x228B22,0x008000],
torii:[0xC23B22,0xB71C1C,0xD32F2F],
ltSt:[0xBDBDBD,0x9E9E9E,0xCFCFCF,0xE0E0E0],
ltLt:[0xFFD54F,0xFFCA28,0xFFC107],
pathSt:[0xA8A8A8,0x9E9E9E,0xBDBDBD,0x8C8C8C],
fuji:[0x455A64,0x37474F,0x546E7A,0x4A5D6A],
snow:[0xFFFFFF,0xF5F5F5,0xECEFF1,0xE8EAF6],
baseRk:[0x6D5D4B,0x5C4A3A,0x7B6B57,0x8B7B67],
petal:[0xFFB7C5,0xFF91A4,0xFFC0CB,0xFF85A2,0xFFD1DC],
bird:[0x2C2C2C,0x3E3E3E,0x1A1A1A],
};
const pc=a=>R.pick(a);
const pcanopy=[...P.p1,...P.p2,...P.p3,...P.p4];
// ─── TERRAIN ───
function genTerrain(){
setStatus('sculpting terrain…');
const rad=36;
for(let x=-rad;x<=rad;x++){
for(let z=-rad;z<=rad;z++){
const d=Math.sqrt(x*x+z*z);if(d>rad)continue;
const nd=d/rad;
const nh=(R.next()*0.3+R.next()*0.2)*(1-nd*0.5);
const topY=Math.floor(nh-nd*3);
const hw=Math.sqrt(Math.max(0,rad*rad-d*d));
const botY=topY-Math.floor(hw*0.55+R.next());
for(let y=botY;y<=topY;y++){
if(y===topY)av(x,y,z,pc(nd>0.7?P.darkGrass:P.grass));
else if(y>=topY-2)av(x,y,z,pc(P.earth));
else if(y>=botY+2)av(x,y,z,pc(R.chance(0.5)?P.earth:P.baseRk));
else av(x,y,z,pc(P.baseRk));
}
}
}
}
// ─── PAGODA ───
function genPagoda(){
setStatus('building pagoda…');
const by=3;
// Multi-layered stone foundation
for(let x=-8;x<=8;x++)for(let z=-8;z<=8;z++){
if(Math.abs(x)+Math.abs(z)>15)continue;
av(x,by-1,z,pc(P.stone));
av(x,by,z,pc(P.stone));
}
for(let x=-7;x<=7;x++)for(let z=-7;z<=7;z++){
if(x*x+z*z>56)continue;
for(let y=by-2;y<=by+1;y++)av(x,y,z,pc(P.stone));
}
// Stone steps leading to pagoda entrance (south side)
for(let s=0;s<4;s++){
for(let x=-2;x<=2;x++)av(x,by+s,8+s,pc(P.stone));
}
const tiers=[
{s:5,wh:5,oh:6,rh:2},{s:4,wh:4,oh:5,rh:2},
{s:3,wh:4,oh:4,rh:2},{s:2,wh:3,oh:4,rh:2},{s:2,wh:3,oh:3,rh:2},
];
let cy=by+2;
for(const tier of tiers){
const s=tier.s;
for(let x=-s;x<=s;x++)for(let z=-s;z<=s;z++)av(x,cy,z,pc(P.pagDark));
for(let y=cy+1;y<=cy+tier.wh;y++){
for(let x=-s;x<=s;x++)for(let z=-s;z<=s;z++){
const ax=Math.abs(x),az=Math.abs(z);
if(ax<s&&az<s)continue;
if(ax!==s&&az!==s)continue;
if(ax===s&&az===s){av(x,y,z,pc(P.pagRed));continue;}
if(y===cy+1||y===cy+tier.wh){av(x,y,z,pc(P.pagRed));continue;}
if(R.chance(0.65))av(x,y,z,pc(P.win));
else av(x,y,z,pc(P.pagRed));
}
}
const bs=s+1,bly=cy+tier.wh;
for(let x=-bs;x<=bs;x++)for(let z=-bs;z<=bs;z++){
if(Math.abs(x)<bs-1&&Math.abs(z)<bs-1)continue;
if(Math.abs(x)>bs||Math.abs(z)>bs)continue;
av(x,bly,z,pc(P.pagDark));
if(Math.abs(x)===bs&&Math.abs(z)===bs)av(x,bly+1,z,pc(P.pagRed));
}
cy=bly+1;
const robY=cy,rh=tier.rh,oh=tier.oh;
for(let ry=0;ry<rh+1;ry++){
const rs=Math.floor(oh-ry*0.3);
if(rs<1)break;
for(let x=-rs;x<=rs;x++)for(let z=-rs;z<=rs;z++){
const cd=Math.abs(x)+Math.abs(z);
const ed=Math.max(Math.abs(x),Math.abs(z));
if(cd>rs*1.8)continue;
// Upturned eaves — corners curve up prominently
const cornerFactor=(ed/rs);
const eaveRise=cornerFactor>0.7?(cornerFactor-0.7)*4:0;
const upY=ry+Math.floor(eaveRise);
if(!hv(x,robY+ry,z))av(x,robY+ry,z,pc(ry===0&&ed>=rs-1?P.roofDk:P.roof));
if(upY>ry&&!hv(x,robY+upY,z))av(x,robY+upY,z,pc(P.roofDk));
// Gold trim on edges
if(ry===0&&ed>=rs-1&&!hv(x,robY+rh,z)&&!hv(x,robY+rh+1,z))
av(x,robY+rh,z,pc(P.gold));
// Corner ornaments (鬼瓦 oni-gawara)
if(ry===0&&ed===rs&&!hv(x,robY+rh+1,z))
av(x,robY+rh+1,z,pc(P.gold));
}
}
const rgY=robY+rh+1,ls=Math.floor(oh-(rh-1)*0.5);
for(let i=-ls;i<=ls;i++){
if(!hv(i,rgY,-ls))av(i,rgY,-ls,pc(P.roofDk));
if(!hv(i,rgY,ls))av(i,rgY,ls,pc(P.roofDk));
if(!hv(-ls,rgY,i))av(-ls,rgY,i,pc(P.roofDk));
if(!hv(ls,rgY,i))av(ls,rgY,i,pc(P.roofDk));
}
cy=rgY+1;
}
const sbY=cy,sh=8;
for(let y=0;y<sh;y++){
const rr=y<3?1:0;
if(y%2===0)for(let x=-rr-1;x<=rr+1;x++)for(let z=-rr-1;z<=rr+1;z++){
if(x===0&&z===0)continue;
if(Math.abs(x)+Math.abs(z)<=rr+1)av(x,sbY+y,z,pc(P.gold));
}
av(0,sbY+y,0,pc(P.gold));
}
av(0,sbY+sh,0,pc(P.gold));
av(0,sbY+sh+1,0,pc(P.gold));
}
// ─── CHERRY TREES ───
function genCherryTree(cx,cz,h,cr){
const by=2;let tx=cx,tz=cz;
// Trunk with natural lean
const leanX=(R.next()-0.5)*2,leanZ=(R.next()-0.5)*2;
for(let y=by;y<by+h;y++){
av(tx,y,tz,pc(P.trunk));
if(y<by+3)for(const[dx,dz]of[[-1,0],[1,0],[0,-1],[0,1]])
if(R.chance(0.5))av(tx+dx,y,tz+dz,pc(P.trunk));
if(y<by+2&&R.chance(0.3)){
av(tx+1,y,tz,pc(P.trunk));av(tx-1,y,tz,pc(P.trunk));
av(tx,y,tz+1,pc(P.trunk));av(tx,y,tz-1,pc(P.trunk));
}
// More branches
if(y>by+h*0.4&&R.chance(0.12)){
const dir=R.int(0,3),len=R.int(1,4);
for(let b=1;b<=len;b++){
let bx=tx,bz=tz,by2=y+b*0.3;
if(dir===0)bx+=b;else if(dir===1)bx-=b;else if(dir===2)bz+=b;else bz-=b;
av(bx,Math.floor(by2),bz,pc(P.trunk));
}
}
if(R.chance(0.12)){tx+=R.pick([-1,0,0,0,1]);tz+=R.pick([-1,0,0,0,1]);}
}
// Canopy — fluffy, irregular shape using multiple overlapping spheres
const ty=by+h;
// Main canopy sphere
const canopyCenters=[
{x:cx,z:cz,y:ty+cr*0.3,r:cr},
{x:cx+cr*0.4,z:cz,y:ty+cr*0.2,r:cr*0.7},
{x:cx-cr*0.3,z:cz+cr*0.3,y:ty+cr*0.2,r:cr*0.65},
{x:cx,z:cz-cr*0.4,y:ty+cr*0.15,r:cr*0.6},
{x:cx+leanX*0.3,z:cz+leanZ*0.3,y:ty+cr*0.5,r:cr*0.5},
];
for(const cc of canopyCenters){
for(let x=cc.x-cc.r-1;x<=cc.x+cc.r+1;x++)
for(let z=cc.z-cc.r-1;z<=cc.z+cc.r+1;z++)
for(let y=cc.y-cc.r;y<=cc.y+cc.r;y++){
const dx=x-cc.x,dz=z-cc.z,dy=y-cc.y;
const dist=Math.sqrt(dx*dx+dz*dz+dy*dy);
if(dist<cc.r+R.next()*0.8&&!hv(x,y,z)){
if(dist<cc.r*0.7||R.chance(0.8))av(x,y,z,R.pick(pcanopy));
}
}
}
}
function genCherryGarden(){
setStatus('planting cherry trees…');
const trees=[
{x:-14,z:-8,h:9,r:5},{x:-12,z:-12,h:7,r:4},{x:-16,z:-5,h:10,r:5},{x:-10,z:-6,h:8,r:4},
{x:14,z:-10,h:8,r:5},{x:16,z:-6,h:10,r:5},{x:12,z:-13,h:7,r:4},
{x:-6,z:-18,h:9,r:4},{x:6,z:-17,h:8,r:5},{x:0,z:-20,h:10,r:5},
{x:10,z:12,h:7,r:4},{x:13,z:15,h:9,r:5},{x:-10,z:10,h:8,r:4},{x:-14,z:14,h:7,r:5},
{x:-20,z:0,h:8,r:4},{x:20,z:-2,h:9,r:5},{x:-8,z:20,h:7,r:4},
{x:8,z:22,h:8,r:4},{x:-18,z:-15,h:6,r:3},{x:18,z:-14,h:7,r:3},
];
for(const t of trees)genCherryTree(t.x,t.z,t.h,t.r);
}
// ─── GREEN TREES ───
function genGreenTree(cx,cz,h,r){
const by=2;
for(let y=by;y<by+h;y++){av(cx,y,cz,pc(P.trunk));if(y<by+2&&R.chance(0.5))av(cx+1,y,cz,pc(P.trunk));}
const ty=by+h;
for(let x=cx-r;x<=cx+r;x++)for(let z=cz-r;z<=cz+r;z++){
const hz=Math.sqrt((x-cx)**2+(z-cz)**2);if(hz>r)continue;
const ch=Math.floor(Math.sqrt(Math.max(0,r*r-hz*hz))*1.2);
for(let y=ty;y<ty+ch;y++)if(R.chance(0.9))
av(x,y,z,R.pick([...P.g1,...P.g2,...P.g3]));
}
}
function genShrub(cx,cz,sz){
const by=2;
for(let x=-sz;x<=sz;x++)for(let z=-sz;z<=sz;z++)for(let y=0;y<=sz;y++){
if(x*x+z*z+y*y>sz*sz+R.next())continue;
if(!hv(cx+x,by+y,cz+z))av(cx+x,by+y,cz+z,R.pick([...P.g2,...P.g3]));
}
}
function genGreenery(){
setStatus('planting shrubbery…');
for(const t of[{x:-22,z:-14,h:7,r:3},{x:22,z:-15,h:8,r:3},{x:-18,z:12,h:6,r:3},{x:18,z:10,h:7,r:3},{x:-24,z:-4,h:6,r:3},{x:24,z:4,h:7,r:3}])
genGreenTree(t.x,t.z,t.h,t.r);
for(let i=0;i<20;i++){
const a=R.next()*Math.PI*2,d=R.range(10,30);
genShrub(Math.floor(Math.cos(a)*d),Math.floor(Math.sin(a)*d),R.int(1,2));
}
}
// ─── STONE PATH ───
function genPath(){
const pts=[];let x=0,z=28;
for(let i=0;i<28;i++){pts.push({x,z});x+=R.pick([-1,0,0,1,0]);z-=R.pick([1,1,1,2]);if(R.chance(0.25))x+=R.pick([-1,0,1]);}
for(let i=0;i<pts.length;i++){
const p=pts[i],pw=i<5?3:2;
for(let dx=-pw;dx<=pw;dx++)for(let dz=-1;dz<=1;dz++){
if(dx*dx+dz*dz>pw*pw)continue;
if(R.chance(0.88)){av(p.x+dx,2,p.z+dz,pc(P.pathSt));if(R.chance(0.35))av(p.x+dx,3,p.z+dz,pc(P.pathSt));}
}
}
// Stone markers at intervals
for(let i=5;i<pts.length-3;i+=4){
const p=pts[i],pw=i<5?3:2;
for(let s of[-1,1]){
if(!hv(p.x+pw+1,2,p.z+s)){av(p.x+pw+1,2,p.z+s,pc(P.pathSt));av(p.x+pw+1,3,p.z+s,pc(P.pathSt));}
if(!hv(p.x-pw-1,2,p.z+s)){av(p.x-pw-1,2,p.z+s,pc(P.pathSt));av(p.x-pw-1,3,p.z+s,pc(P.pathSt));}
}
}
}
// ─── DECO PLANTS ───
function genDecoPlants(){
// Small flowering bushes around pagoda base
const spots=[
{x:-10,z:3},{x:10,z:3},{x:-9,z:-3},{x:9,z:-3},
{x:-7,z:8},{x:7,z:8},{x:-11,z:-8},{x:11,z:-8},
];
for(const sp of spots){
for(let dx=-1;dx<=1;dx++)for(let dz=-1;dz<=1;dz++){
if(dx*dx+dz*dz>2)continue;
if(R.chance(0.7)){
av(sp.x+dx,2,sp.z+dz,pc(P.g3));
av(sp.x+dx,3,sp.z+dz,R.chance(0.4)?R.pick(pcanopy):pc(P.g2));
}
}
}
// Fallen petal patches on ground
for(let i=0;i<20;i++){
const a=R.next()*Math.PI*2,d=R.range(5,28);
const px=Math.floor(Math.cos(a)*d),pz=Math.floor(Math.sin(a)*d);
if(!hv(px,3,pz))av(px,3,pz,R.chance(0.5)?R.pick(pcanopy):pc(P.pathSt));
}
// Small reflecting pool east of pagoda
const px=15,pz=5;
for(let x=px-3;x<=px+3;x++)for(let z=pz-2;z<=pz+2;z++){
if((x-px)*(x-px)+(z-pz)*(z-pz)>10)continue;
av(x,2,z,0x1565C0); // Water blue
}
// Pool edge stones
for(let x=px-4;x<=px+4;x++)for(let z=pz-3;z<=pz+3;z++){
if((x-px)*(x-px)+(z-pz)*(z-pz)>10&&(x-px)*(x-px)+(z-pz)*(z-pz)<16){
if(R.chance(0.6))av(x,2,z,pc(P.stone));
}
}
}
// ─── TORII GATE ───
function genTorii(){
const tx=0,tz=28,by=2;
for(let y=by;y<by+9;y++){
av(tx-5,y,tz,pc(P.torii));av(tx+5,y,tz,pc(P.torii));
if(y<by+2){av(tx-5,y,tz+1,pc(P.torii));av(tx+5,y,tz+1,pc(P.torii));}
}
for(let x=-6;x<=6;x++)av(tx+x,by+7,tz,pc(P.torii));
for(let x=-7;x<=7;x++){
av(tx+x,by+9,tz,pc(P.torii));
if(Math.abs(x)>=6)av(tx+x,by+10,tz,pc(P.torii));
}
av(tx,by+8,tz,pc(P.pagDark));
}
// ─── STONE LANTERNS ───
function genLantern(x,z){
const by=2;
av(x,by,z,pc(P.ltSt));av(x-1,by,z,pc(P.ltSt));av(x+1,by,z,pc(P.ltSt));
av(x,by,z-1,pc(P.ltSt));av(x,by,z+1,pc(P.ltSt));
for(let y=by+1;y<=by+3;y++)av(x,y,z,pc(P.ltSt));
av(x,by+4,z,pc(P.ltLt));av(x,by+4,z-1,pc(P.ltSt));av(x,by+4,z+1,pc(P.ltSt));
av(x-1,by+4,z,pc(P.ltSt));av(x+1,by+4,z,pc(P.ltSt));
av(x-1,by+4,z-1,pc(P.ltSt));av(x+1,by+4,z+1,pc(P.ltSt));
av(x-1,by+4,z+1,pc(P.ltSt));av(x+1,by+4,z-1,pc(P.ltSt));
for(let dx=-2;dx<=2;dx++)for(let dz=-2;dz<=2;dz++)
if(Math.abs(dx)+Math.abs(dz)<=3)av(x+dx,by+5,z+dz,pc(P.ltSt));
av(x,by+6,z,pc(P.ltSt));
}
function placeLanterns(){
for(const lp of[{x:-3,z:22},{x:3,z:22},{x:-5,z:14},{x:5,z:14},{x:-8,z:6},{x:8,z:6},{x:12,z:0},{x:-12,z:0}])
genLantern(lp.x,lp.z);
}
// ─── ROCKS ───
function genRock(cx,cz,sz){
const by=2;
for(let x=-sz;x<=sz;x++)for(let z=-sz;z<=sz;z++){
const h=Math.floor(Math.sqrt(Math.max(0,sz*sz-x*x-z*z))*0.7);
for(let y=0;y<=h;y++)if(R.chance(0.85))
av(cx+x,by+y,cz+z,pc(R.chance(0.3)?P.darkStone:P.stone));
}
}
function placeRocks(){
for(let i=0;i<12;i++){
const a=R.next()*Math.PI*2,d=R.range(8,32);
genRock(Math.floor(Math.cos(a)*d),Math.floor(Math.sin(a)*d),R.int(1,3));
}
}
// ─── MOUNT FUJI ───
function genFuji(){
const fx=-60,fz=-70,by=-1,h=25,br=18;
for(let y=0;y<h;y++){
const r=Math.floor(br*(1-y/h));
for(let x=-r;x<=r;x++)for(let z=-r;z<=r;z++){
if(x*x+z*z>r*r)continue;
if(y>h*0.6){if(R.chance(0.85))av(fx+x,by+y,fz+z,pc(P.snow));}
else if(R.chance(0.7))av(fx+x,by+y,fz+z,pc(P.fuji));
}
}
}
// ─── BUILD VOXEL MESH ───
let windowMatList=[];
let totalVoxels=0;
function buildVoxels(scene){
setStatus('rendering voxels…');
const groups=new Map();
for(const v of VM.values()){
const isWin=P.win.includes(v.c);
const key=isWin?'__window__':v.c;
if(!groups.has(key))groups.set(key,[]);
groups.get(key).push(v);
}
const geo=new THREE.BoxGeometry(1,1,1);
for(const[key,voxels]of groups){
const isWin=key==='__window__';
const mat=new THREE.MeshStandardMaterial({
color:isWin?0xFFC107:new THREE.Color(key),
roughness:0.8,metalness:0.05,flatShading:true,
});
if(isWin){
mat.emissive=new THREE.Color(0x000000);
mat.emissiveIntensity=0;
windowMatList.push(mat);
}
const mesh=new THREE.InstancedMesh(geo,mat,voxels.length);
const dummy=new THREE.Object3D();
for(let i=0;i<voxels.length;i++){
const v=voxels[i];dummy.position.set(v.x,v.y,v.z);dummy.updateMatrix();
mesh.setMatrixAt(i,dummy.matrix);
}
mesh.instanceMatrix.needsUpdate=true;
mesh.castShadow=true;mesh.receiveShadow=true;
scene.add(mesh);
totalVoxels+=voxels.length;
}
VM.clear();
document.getElementById('voxel-count').textContent=`voxels: ${totalVoxels.toLocaleString()}`;
}
// ─── WATER ───
let waterMesh;
function genWater(scene){
const geo=new THREE.PlaneGeometry(200,200,80,80);
const mat=new THREE.MeshStandardMaterial({
color:0x1565C0,roughness:0.05,metalness:0.4,transparent:true,opacity:0.75,
side:THREE.DoubleSide,
});
waterMesh=new THREE.Mesh(geo,mat);
waterMesh.rotation.x=-Math.PI/2;waterMesh.position.y=-2.5;
waterMesh.receiveShadow=true;scene.add(waterMesh);
}
function animWater(t){
if(!waterMesh)return;
const pos=waterMesh.geometry.attributes.position;
for(let i=0;i<pos.count;i++){
const x=pos.getX(i),z=pos.getY(i);
pos.setZ(i,Math.sin(x*0.08+t*0.8)*0.3+Math.sin(z*0.1+t*0.6)*0.2+Math.sin((x+z)*0.05+t*1.1)*0.15);
}
pos.needsUpdate=true;waterMesh.geometry.computeVertexNormals();
}
// ─── BIRDS ───
let birds=[];
function genBirds(scene){
for(let i=0;i<5;i++){
const g=new THREE.Group();
const body=new THREE.Mesh(new THREE.BoxGeometry(0.5,0.2,0.2),new THREE.MeshStandardMaterial({color:P.bird[0],roughness:0.8}));
const wL=new THREE.Mesh(new THREE.BoxGeometry(0.6,0.1,0.2),new THREE.MeshStandardMaterial({color:P.bird[1],roughness:0.8}));
wL.position.set(-0.4,0.05,0);
const wR=wL.clone();wR.position.set(0.4,0.05,0);
g.add(body,wL,wR);
const px=R.range(-30,30),pz=R.range(-30,30);
g.position.set(px,R.range(25,40),pz);
scene.add(g);
birds.push({g,wL,wR,or:Math.sqrt(px*px+pz*pz),oa:Math.atan2(pz,px),os:R.range(0.05,0.15),oy:g.position.y,ot:R.range(0.2,0.5)});
}
}
function animBirds(t){
for(const b of birds){
b.oa+=b.os*0.016;
b.g.position.x=Math.cos(b.oa)*b.or;b.g.position.z=Math.sin(b.oa)*b.or;
b.g.position.y=b.oy+Math.sin(t*2+b.oa)*1.5;
b.g.rotation.y=-b.oa+Math.PI/2;b.g.rotation.z=Math.sin(t*3)*b.ot*0.2;
const fl=Math.sin(t*8)*0.4;b.wL.rotation.z=fl;b.wR.rotation.z=-fl;
}
}
// ─── PETALS ───
let petalSystem=null,petalVisible=true,mistSystem=null,starsSystem=null;
function genPetals(scene){
const N=1200;
const geo=new THREE.BufferGeometry();
const pos=new Float32Array(N*3),vel=new Float32Array(N*3),col=new Float32Array(N*3),sz=new Float32Array(N);
for(let i=0;i<N;i++){
pos[i*3]=R.range(-40,40);pos[i*3+1]=R.range(3,35);pos[i*3+2]=R.range(-40,40);
vel[i*3]=R.range(-0.3,0.3);vel[i*3+1]=R.range(-0.4,-0.08);vel[i*3+2]=R.range(-0.2,0.2);
const c=new THREE.Color(pc(P.petal));col[i*3]=c.r;col[i*3+1]=c.g;col[i*3+2]=c.b;
sz[i]=R.range(0.2,0.6);
}
geo.setAttribute('position',new THREE.BufferAttribute(pos,3));
geo.setAttribute('color',new THREE.BufferAttribute(col,3));
geo.setAttribute('size',new THREE.BufferAttribute(sz,1));
petalSystem=new THREE.Points(geo,new THREE.PointsMaterial({size:0.35,vertexColors:true,transparent:true,opacity:0.8,sizeAttenuation:true,depthWrite:false}));
petalSystem.userData.vel=vel;petalSystem.userData.sizes=sz;scene.add(petalSystem);
}
function animPetals(){
if(!petalSystem||!petalVisible)return;
const pos=petalSystem.geometry.attributes.position,vel=petalSystem.userData.vel;
for(let i=0;i<pos.count;i++){
let x=pos.getX(i)+vel[i*3]*0.016,y=pos.getY(i)+vel[i*3+1]*0.016,z=pos.getZ(i)+vel[i*3+2]*0.016;
vel[i*3]+=(Math.random()-0.5)*0.02;vel[i*3+2]+=(Math.random()-0.5)*0.02;
vel[i*3]=Math.max(-1,Math.min(1,vel[i*3]));vel[i*3+2]=Math.max(-1,Math.min(1,vel[i*3+2]));
if(y<-3){y=R.range(20,35);x=R.range(-35,35);z=R.range(-35,35);}
pos.setXYZ(i,x,y,z);
}
pos.needsUpdate=true;
}
// ─── MIST ───
function genMist(scene){
const N=300;
const geo=new THREE.BufferGeometry();
const pos=new Float32Array(N*3),vel=new Float32Array(N*3);
for(let i=0;i<N;i++){
const a=Math.random()*Math.PI*2,d=R.range(5,35);
pos[i*3]=Math.cos(a)*d;pos[i*3+1]=R.range(-1,3);pos[i*3+2]=Math.sin(a)*d;
vel[i*3]=R.range(-0.1,0.1);vel[i*3+1]=R.range(0.01,0.05);vel[i*3+2]=R.range(-0.1,0.1);
}
geo.setAttribute('position',new THREE.BufferAttribute(pos,3));
mistSystem=new THREE.Points(geo,new THREE.PointsMaterial({size:0.8,color:0xffffff,transparent:true,opacity:0.12,sizeAttenuation:true,depthWrite:false}));
mistSystem.userData.vel=vel;scene.add(mistSystem);
}
function animMist(t){
if(!mistSystem)return;
const pos=mistSystem.geometry.attributes.position,vel=mistSystem.userData.vel;
for(let i=0;i<pos.count;i++){
let x=pos.getX(i)+vel[i*3]*0.016,y=pos.getY(i)+vel[i*3+1]*0.016,z=pos.getZ(i)+vel[i*3+2]*0.016;
vel[i*3]+=(Math.random()-0.5)*0.005;vel[i*3+2]+=(Math.random()-0.5)*0.005;
if(y>5){y=R.range(-1,1);}
pos.setXYZ(i,x,y,z);
}
pos.needsUpdate=true;
if(curTime==='dawn'||curTime==='night')mistSystem.material.opacity=0.18;
else mistSystem.material.opacity=0.08;
}
// ─── STARS (visible at night) ───
function genStars(scene){
const N=400;
const geo=new THREE.BufferGeometry();
const pos=new Float32Array(N*3);
for(let i=0;i<N;i++){
const theta=R.next()*Math.PI*2;
const phi=R.range(0.1,Math.PI*0.45);
const r=120;
pos[i*3]=r*Math.sin(phi)*Math.cos(theta);
pos[i*3+1]=r*Math.cos(phi)+10;
pos[i*3+2]=r*Math.sin(phi)*Math.sin(theta);
}
geo.setAttribute('position',new THREE.BufferAttribute(pos,3));
starsSystem=new THREE.Points(geo,new THREE.PointsMaterial({size:0.4,color:0xffffff,transparent:true,opacity:0,sizeAttenuation:true,depthWrite:false}));
scene.add(starsSystem);
}
function animStars(){
if(!starsSystem)return;
const target=curTime==='night'?0.7:curTime==='dusk'?0.15:0;
starsSystem.material.opacity+=(target-starsSystem.material.opacity)*0.05;
}
// ─── TIME OF DAY ───
const TP={
dawn:{label:'Dawn — 06:00',sky:0xFF8C69,fog:0xFFB088,fd:0.008,ac:0xFFD0A0,ai:0.4,sc:0xFF9966,si:1.2,sp:new THREE.Vector3(40,10,30),wg:0.3},
day:{label:'Day — 12:00',sky:0x87CEEB,fog:0xC0D8F0,fd:0.005,ac:0xffffff,ai:0.6,sc:0xFFF5E0,si:2.0,sp:new THREE.Vector3(20,50,20),wg:0},
dusk:{label:'Dusk — 18:30',sky:0xCC7744,fog:0xDD9966,fd:0.01,ac:0xFFCC99,ai:0.35,sc:0xFF6633,si:1.0,sp:new THREE.Vector3(-40,8,30),wg:0.5},
night:{label:'Night — 00:00',sky:0x0A0A1A,fog:0x15152A,fd:0.015,ac:0x2233AA,ai:0.15,sc:0x4466AA,si:0.3,sp:new THREE.Vector3(-10,5,-30),wg:1.0},
};
let curTime='day',autoCycle=false,cycleTimer=0;
const timeLabelEl=document.getElementById('time-label');
let dirLight,ambientLight,sky,pagodaLight;
function setTime(p){
const t=TP[p];if(!t)return;curTime=p;
timeLabelEl.textContent=t.label;
document.querySelectorAll('.time-btn').forEach(b=>b.classList.toggle('active',b.dataset.time===p));
scene.background=new THREE.Color(t.sky);scene.fog.color.set(t.fog);scene.fog.density=t.fd;
ambientLight.color.set(t.ac);ambientLight.intensity=t.ai;
dirLight.color.set(t.sc);dirLight.intensity=t.si;dirLight.position.copy(t.sp);
for(const m of windowMatList){
m.emissive.set(t.wg>0.1?0xFF8C00:0x000000);
m.emissiveIntensity=t.wg*2;
}
pagodaLight.intensity=t.wg*3;
}
// ─── CAMERA ───
let cinFly=false,cinTime=0,autoOrbit=false;
function startFly(){if(cinFly)return;cinFly=true;cinTime=0;}
function updateFly(d){
if(!cinFly)return;cinTime+=d;
const t=Math.min(cinTime/15,1);
if(t>=1){cinFly=false;document.getElementById('btn-fly').classList.remove('on');return;}
// Phase 1: rise up from ground (0-0.3), Phase 2: circle high (0.3-0.7), Phase 3: descend (0.7-1.0)
let dist,h,a;
if(t<0.3){
const p=t/0.3;
dist=80-p*30;h=5+p*25;a=p*Math.PI*0.5;
}else if(t<0.7){
const p=(t-0.3)/0.4;
dist=50;h=30+Math.sin(p*Math.PI)*5;a=Math.PI*0.5+p*Math.PI*1.5;
}else{
const p=(t-0.7)/0.3;
dist=50+p*20;h=30-p*20;a=Math.PI*2+p*Math.PI*0.25;
}
camera.position.set(Math.cos(a)*dist,h,Math.sin(a)*dist);
controls.target.lerp(new THREE.Vector3(0,12,0),d*0.5);
}
// ─── SCENE SETUP ───
const scene=new THREE.Scene();
scene.background=new THREE.Color(TP.day.sky);
scene.fog=new THREE.FogExp2(TP.day.fog,TP.day.fd);
const camera=new THREE.PerspectiveCamera(50,innerWidth/innerHeight,0.5,500);
camera.position.set(55,30,55);
const renderer=new THREE.WebGLRenderer({antialias:true,powerPreference:'high-performance'});
renderer.setSize(innerWidth,innerHeight);
renderer.setPixelRatio(Math.min(devicePixelRatio,2));
renderer.shadowMap.enabled=true;renderer.shadowMap.type=THREE.PCFSoftShadowMap;
renderer.toneMapping=THREE.ACESFilmicToneMapping;renderer.toneMappingExposure=1.0;
document.body.appendChild(renderer.domElement);
const controls=new OrbitControls(camera,renderer.domElement);
controls.enableDamping=true;controls.dampingFactor=0.08;controls.target.set(0,10,0);
controls.minDistance=15;controls.maxDistance=150;controls.maxPolarAngle=Math.PI*0.85;controls.update();
ambientLight=new THREE.AmbientLight(TP.day.ac,TP.day.ai);scene.add(ambientLight);
dirLight=new THREE.DirectionalLight(TP.day.sc,TP.day.si);
dirLight.position.copy(TP.day.sp);dirLight.castShadow=true;
dirLight.shadow.mapSize.set(2048,2048);
dirLight.shadow.camera.left=-60;dirLight.shadow.camera.right=60;
dirLight.shadow.camera.top=60;dirLight.shadow.camera.bottom=-60;
dirLight.shadow.camera.near=1;dirLight.shadow.camera.far=200;dirLight.shadow.bias=-0.001;
scene.add(dirLight);
// Fill light for per-face contrast
const fillLight=new THREE.DirectionalLight(0x8899BB,0.4);
fillLight.position.set(-30,20,-20);scene.add(fillLight);
scene.add(new THREE.HemisphereLight(0x87CEEB,0x3CB371,0.25));
// Warm point light inside pagoda
pagodaLight=new THREE.PointLight(0xFF8C00,0,20,2);
pagodaLight.position.set(0,8,0);scene.add(pagodaLight);
// ─── GENERATE ───
setStatus('sculpting terrain…');
genTerrain();genPagoda();genCherryGarden();genGreenery();genPath();
genTorii();placeLanterns();placeRocks();genDecoPlants();genFuji();
buildVoxels(scene);
genWater(scene);genBirds(scene);genPetals(scene);genMist(scene);genStars(scene);
setTime('day');
// Fade out loading
setTimeout(()=>{document.getElementById('loading').classList.add('done');},500);
setTimeout(()=>{document.getElementById('loading').remove();},1500);
// ─── EVENTS ───
window.addEventListener('resize',()=>{
camera.aspect=innerWidth/innerHeight;camera.updateProjectionMatrix();
renderer.setSize(innerWidth,innerHeight);
});
document.querySelectorAll('.time-btn').forEach(b=>b.addEventListener('click',()=>{
const t=b.dataset.time;if(t==='auto'){autoCycle=!autoCycle;cycleTimer=0;b.classList.toggle('on',autoCycle);}
else{autoCycle=false;setTime(t);}
}));
document.getElementById('btn-petals').addEventListener('click',e=>{
petalVisible=!petalVisible;e.target.classList.toggle('on',petalVisible);
});
document.getElementById('btn-autoorbit').addEventListener('click',e=>{
autoOrbit=!autoOrbit;e.target.classList.toggle('on',autoOrbit);
});
document.getElementById('btn-fly').addEventListener('click',e=>{
startFly();e.target.classList.add('on');
});
window.addEventListener('keydown',e=>{
const km={'1':'dawn','2':'day','3':'dusk','4':'night'};
if(km[e.key]){autoCycle=false;setTime(km[e.key]);}
});
// ─── ANIMATE ───
const clock=new THREE.Clock();
function animate(){
requestAnimationFrame(animate);
const d=clock.getDelta(),t=clock.getElapsedTime();
controls.update();
if(autoOrbit){
const r=camera.position.distanceTo(controls.target);
const phi=Math.acos(Math.max(-1,Math.min(1,(camera.position.y-controls.target.y)/r)));
controls.autoRotate=true;controls.autoRotateSpeed=1.5;
}else{controls.autoRotate=false;}
controls.update();
updateFly(d);
if(autoCycle){
cycleTimer+=d;
if(cycleTimer>=15){cycleTimer=0;const ph=['dawn','day','dusk','night'];setTime(ph[(ph.indexOf(curTime)+1)%4]);}
}
animWater(t);animBirds(t);animPetals();animMist(t);animStars();
renderer.render(scene,camera);
}
animate();
</script>
</body>
</html>

Design and create a very creative, elaborate, and detailed voxel art scene of a pagoda in a beautiful garden with trees, including some cherry blossoms. Make the scene impressive and varied and use colorful voxels. Use whatever libraries to get this done but make sure I can paste it all into a single HTML file and open it in Chrome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment