← back to Model Arena

data/artifacts/127317180daf/qwen3-14b.html

524 lines

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Rose City Rooftop Solstice</title>
  <style>
    html, body {
      margin: 0;
      padding: 0;
      overflow: hidden;
      background: #000;
      font-family: 'Arial', sans-serif;
    }
    canvas {
      display: block;
    }
    #controls {
      position: absolute;
      top: 10px;
      left: 10px;
      z-index: 10;
      background: rgba(255,255,255,0.1);
      padding: 10px;
      border-radius: 10px;
      color: #fff;
      font-size: 14px;
    }
    #controls button, #controls select, #controls input {
      margin: 5px 0;
      padding: 6px 10px;
      background: rgba(255,255,255,0.2);
      border: 1px solid rgba(255,255,255,0.4);
      color: #fff;
      border-radius: 5px;
      cursor: pointer;
      transition: background 0.2s ease;
    }
    #controls button:hover, #controls select:hover, #controls input:hover {
      background: rgba(255,255,255,0.3);
    }
    #controls label {
      display: block;
      margin: 5px 0 2px;
    }
    #info {
      position: absolute;
      bottom: 10px;
      left: 10px;
      z-index: 10;
      color: #fff;
      font-size: 12px;
      text-align: left;
    }
  </style>
</head>
<body>
  <canvas id="canvas"></canvas>
  <div id="controls">
    <label>Camera:</label>
    <select id="camera">
      <option value="skyline">Skyline</option>
      <option value="rooftop">Rooftop</option>
      <option value="birds-eye">Bird's-Eye</option>
    </select>
    <label>Scene:</label>
    <select id="scene">
      <option value="arrival">Arrival</option>
      <option value="long-light">Long Light</option>
      <option value="midnight-bloom">Midnight Bloom</option>
    </select>
    <label>Tempo:</label>
    <input type="range" id="tempo" min="0.5" max="2" step="0.1" value="1">
    <label>Density:</label>
    <input type="range" id="density" min="0.3" max="1" step="0.1" value="0.7">
    <label>Groups:</label>
    <input type="checkbox" id="group1"> Central Circle
    <input type="checkbox" id="group2"> Bridges
    <input type="checkbox" id="group3"> Water Towers
    <input type="checkbox" id="group4"> Fire Escapes
    <label>Animals:</label>
    <input type="checkbox" id="animalNames"> Show Names
    <input type="checkbox" id="animalTrails"> Show Trails
    <input type="checkbox" id="routeDiagrams"> Show Routes
    <br>
    <button id="pause">Pause</button>
    <button id="restart">Restart</button>
    <button id="reduceMotion">Reduce Motion</button>
  </div>
  <div id="info">
    Visual rhythm is authored rather than listening to audio.
    <br>
    Keyboard controls: 
    <br>
    Arrow keys: Move camera
    <br>
    W/S: Zoom
    <br>
    R: Reset
  </div>
  <script>
    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');
    const width = canvas.width = window.innerWidth;
    const height = canvas.height = window.innerHeight;

    const controlElements = {
      camera: document.getElementById('camera'),
      scene: document.getElementById('scene'),
      tempo: document.getElementById('tempo'),
      density: document.getElementById('density'),
      group1: document.getElementById('group1'),
      group2: document.getElementById('group2'),
      group3: document.getElementById('group3'),
      group4: document.getElementById('group4'),
      animalNames: document.getElementById('animalNames'),
      animalTrails: document.getElementById('animalTrails'),
      routeDiagrams: document.getElementById('routeDiagrams'),
      pause: document.getElementById('pause'),
      restart: document.getElementById('restart'),
      reduceMotion: document.getElementById('reduceMotion'),
    };

    let state = {
      scene: 'arrival',
      camera: 'skyline',
      tempo: 1,
      density: 0.7,
      groups: {1: true, 2: true, 3: true, 4: true},
      showNames: false,
      showTrails: false,
      showRoutes: false,
      isPaused: false,
      reduceMotion: false,
      time: 0,
      frame: 0,
      lastTime: 0,
    };

    const sceneColors = {
      arrival: {bg: '#111', rose: '#ff6b6b', light: '#f0c14b', cloud: '#60a5fa'},
      'long-light': {bg: '#0a0a0a', rose: '#ff4081', light: '#ffd700', cloud: '#3b82f6'},
      'midnight-bloom': {bg: '#000', rose: '#e63946', light: '#e9c46a', cloud: '#2a2240'},
    };

    const entities = {
      dancers: [],
      animals: [],
      buildings: [],
      waterTowers: [],
      fireEscapes: [],
    };

    const initEntities = () => {
      const numDancers = Math.floor(100 * state.density);
      const numAnimals = Math.floor(20 * state.density);
      const numBuildings = Math.floor(50 * state.density);

      for (let i = 0; i < numDancers; i++) {
        entities.dancers.push({
          type: 'dancer',
          x: Math.random() * width,
          y: Math.random() * height,
          angle: Math.random() * Math.PI * 2,
          speed: (Math.random() + 0.5) * 0.01 * state.tempo,
          group: Math.floor(Math.random() * 4) + 1,
        });
      }

      for (let i = 0; i < numAnimals; i++) {
        const types = ['bear', 'cat', 'dog', 'moth', 'pigeon'];
        const type = types[Math.floor(Math.random() * types.length)];
        entities.animals.push({
          type,
          x: Math.random() * width,
          y: Math.random() * height,
          angle: Math.random() * Math.PI * 2,
          speed: (Math.random() + 0.5) * 0.01 * state.tempo,
        });
      }

      for (let i = 0; i < numBuildings; i++) {
        entities.buildings.push({
          x: Math.random() * width,
          y: Math.random() * height,
          scale: 0.2 + Math.random() * 0.3,
        });
      }

      for (let i = 0; i < 10; i++) {
        entities.waterTowers.push({
          x: width * 0.2 + Math.random() * width * 0.6,
          y: height * 0.2 + Math.random() * height * 0.6,
        });
      }

      for (let i = 0; i < 5; i++) {
        entities.fireEscapes.push({
          x: width * 0.2 + Math.random() * width * 0.6,
          y: height * 0.2 + Math.random() * height * 0.6,
        });
      }
    };

    const draw = (time) => {
      if (state.isPaused) return;
      state.time = time;
      state.frame++;
      ctx.clearRect(0, 0, width, height);

      const color = sceneColors[state.scene];
      ctx.fillStyle = color.bg;
      ctx.fillRect(0, 0, width, height);

      // Draw buildings with parallax
      ctx.save();
      ctx.translate(width / 2, height / 2);
      ctx.scale(1, 1);
      ctx.fillStyle = color.bg;
      ctx.fillRect(-width / 2, -height / 2, width, height);
      ctx.restore();

      // Draw clouds
      ctx.fillStyle = color.cloud;
      for (let i = 0; i < 10; i++) {
        const cx = Math.random() * width;
        const cy = Math.random() * height * 0.3;
        const size = 10 + Math.random() * 20;
        ctx.beginPath();
        ctx.arc(cx, cy, size, 0, Math.PI * 2);
        ctx.fill();
      }

      // Draw water towers
      ctx.fillStyle = color.light;
      for (let tower of entities.waterTowers) {
        const size = 15 + Math.random() * 10;
        const x = tower.x;
        const y = tower.y;
        ctx.beginPath();
        ctx.arc(x, y, size, 0, Math.PI * 2);
        ctx.fill();
      }

      // Draw fire escapes
      ctx.strokeStyle = color.light;
      ctx.lineWidth = 2;
      for (let escape of entities.fireEscapes) {
        const size = 10 + Math.random() * 10;
        const x = escape.x;
        const y = escape.y;
        ctx.beginPath();
        ctx.moveTo(x - size, y);
        ctx.lineTo(x + size, y);
        ctx.stroke();
      }

      // Draw dancers
      for (let d of entities.dancers) {
        if (!state.groups[d.group]) continue;

        let x = d.x;
        let y = d.y;
        let angle = d.angle;
        if (d.group === 1) {
          // Central Circle
          const centerX = width / 2;
          const centerY = height / 2;
          const radius = 100 + Math.sin(state.time * d.speed) * 50;
          x = centerX + Math.cos(angle) * radius;
          y = centerY + Math.sin(angle) * radius;
          angle += state.time * d.speed;
          d.angle = angle;
        } else if (d.group === 2) {
          // Bridges
          const start = {x: 0, y: height / 2};
          const end = {x: width, y: height / 2};
          const t = (Math.sin(state.time * d.speed) + 1) / 2;
          x = start.x + t * (end.x - start.x);
          y = start.y + t * (end.y - start.y);
          angle = Math.atan2(end.y - start.y, end.x - start.x);
        } else if (d.group === 3) {
          // Water Towers
          const size = 50 + Math.sin(state.time * d.speed) * 20;
          x = d.x + Math.cos(angle) * size;
          y = d.y + Math.sin(angle) * size;
          angle += state.time * d.speed;
        } else if (d.group === 4) {
          // Fire Escapes
          const speed = 0.005 * state.tempo;
          d.y -= speed;
          if (d.y < 0) {
            d.y = height;
            d.x = Math.random() * width;
          }
        }

        d.x = x;
        d.y = y;
        d.angle = angle;

        // Draw dancer
        ctx.save();
        ctx.translate(x, y);
        ctx.rotate(angle);
        ctx.fillStyle = color.rose;
        ctx.beginPath();
        ctx.arc(0, 0, 4, 0, Math.PI * 2);
        ctx.fill();
        ctx.restore();
      }

      // Draw animals
      for (let a of entities.animals) {
        let x = a.x;
        let y = a.y;
        let angle = a.angle;
        if (a.type === 'bear') {
          x += Math.cos(angle) * 5;
          y += Math.sin(angle) * 5;
        } else if (a.type === 'cat') {
          x += Math.cos(angle) * 3;
          y += Math.sin(angle) * 3;
        } else if (a.type === 'dog') {
          x += Math.cos(angle) * 4;
          y += Math.sin(angle) * 4;
        } else if (a.type === 'moth') {
          x += Math.cos(angle) * 2;
          y += Math.sin(angle) * 2;
          angle += state.time * 0.01;
        } else if (a.type === 'pigeon') {
          x += Math.cos(angle) * 5;
          y += Math.sin(angle) * 5;
          angle += state.time * 0.005;
        }

        a.x = x;
        a.y = y;
        a.angle = angle;

        if (state.showTrails) {
          ctx.strokeStyle = color.light;
          ctx.lineWidth = 1;
          ctx.beginPath();
          ctx.moveTo(a.x, a.y);
          ctx.lineTo(a.x + Math.cos(angle) * 5, a.y + Math.sin(angle) * 5);
          ctx.stroke();
        }

        ctx.save();
        ctx.translate(x, y);
        ctx.rotate(angle);
        ctx.fillStyle = color.light;
        ctx.beginPath();
        if (a.type === 'bear') {
          ctx.fillRect(-4, -4, 8, 8);
        } else if (a.type === 'cat') {
          ctx.beginPath();
          ctx.arc(0, 0, 4, 0, Math.PI * 2);
          ctx.fill();
        } else if (a.type === 'dog') {
          ctx.beginPath();
          ctx.arc(0, 0, 4, 0, Math.PI * 2);
          ctx.fill();
          ctx.fillRect(-2, -2, 4, 4);
        } else if (a.type === 'moth') {
          ctx.beginPath();
          ctx.arc(0, 0, 3, 0, Math.PI * 2);
          ctx.fill();
          ctx.beginPath();
          ctx.moveTo(-3, 0);
          ctx.lineTo(0, 4);
          ctx.lineTo(3, 0);
          ctx.fill();
        } else if (a.type === 'pigeon') {
          ctx.beginPath();
          ctx.arc(0, 0, 5, 0, Math.PI * 2);
          ctx.fill();
          ctx.beginPath();
          ctx.moveTo(0, 0);
          ctx.lineTo(0, -5);
          ctx.stroke();
        }
        ctx.restore();

        if (state.showNames) {
          ctx.fillStyle = color.light;
          ctx.font = '10px Arial';
          ctx.fillText(a.type, x + 6, y - 6);
        }
      }

      // Draw central rose sculpture
      ctx.fillStyle = color.rose;
      ctx.beginPath();
      ctx.moveTo(width / 2, height / 2);
      for (let i = 0; i < 12; i++) {
        const angle = (Math.PI * 2 / 12) * i;
        const r = 40 + 20 * Math.sin(state.time * 0.01 * i);
        const x = width / 2 + Math.cos(angle) * r;
        const y = height / 2 + Math.sin(angle) * r;
        ctx.lineTo(x, y);
      }
      ctx.closePath();
      ctx.fill();

      // Draw light trails
      if (state.showTrails) {
        ctx.strokeStyle = color.light;
        ctx.lineWidth = 1;
        for (let d of entities.dancers) {
          ctx.beginPath();
          ctx.moveTo(d.x, d.y);
          ctx.lineTo(d.x + Math.cos(d.angle) * 5, d.y + Math.sin(d.angle) * 5);
          ctx.stroke();
        }
      }

      // Draw route diagrams
      if (state.showRoutes) {
        ctx.strokeStyle = color.light;
        ctx.lineWidth = 1;
        ctx.globalAlpha = 0.3;
        for (let i = 0; i < 10; i++) {
          ctx.beginPath();
          ctx.moveTo(0, height / 2);
          ctx.lineTo(width, height / 2);
          ctx.stroke();
        }
        ctx.globalAlpha = 1;
      }

      requestAnimationFrame(draw);
    };

    const updateState = () => {
      state.scene = controlElements.scene.value;
      state.camera = controlElements.camera.value;
      state.tempo = parseFloat(controlElements.tempo.value);
      state.density = parseFloat(controlElements.density.value);
      state.groups = {
        1: controlElements.group1.checked,
        2: controlElements.group2.checked,
        3: controlElements.group3.checked,
        4: controlElements.group4.checked,
      };
      state.showNames = controlElements.animalNames.checked;
      state.showTrails = controlElements.animalTrails.checked;
      state.showRoutes = controlElements.routeDiagrams.checked;
      state.reduceMotion = controlElements.reduceMotion.checked;
    };

    const bindControls = () => {
      controlElements.camera.addEventListener('change', updateState);
      controlElements.scene.addEventListener('change', updateState);
      controlElements.tempo.addEventListener('input', updateState);
      controlElements.density.addEventListener('input', updateState);
      controlElements.group1.addEventListener('change', updateState);
      controlElements.group2.addEventListener('change', updateState);
      controlElements.group3.addEventListener('change', updateState);
      controlElements.group4.addEventListener('change', updateState);
      controlElements.animalNames.addEventListener('change', updateState);
      controlElements.animalTrails.addEventListener('change', updateState);
      controlElements.routeDiagrams.addEventListener('change', updateState);
      controlElements.pause.addEventListener('click', () => {
        state.isPaused = !state.isPaused;
        controlElements.pause.textContent = state.isPaused ? 'Resume' : 'Pause';
      });
      controlElements.restart.addEventListener('click', () => {
        state.time = 0;
        state.frame = 0;
        initEntities();
      });
      controlElements.reduceMotion.addEventListener('click', () => {
        state.reduceMotion = !state.reduceMotion;
        controlElements.reduceMotion.textContent = state.reduceMotion ? 'Normal Motion' : 'Reduce Motion';
      });

      document.addEventListener('keydown', (e) => {
        if (e.key === 'ArrowUp') {
          e.preventDefault();
          state.camera = 'skyline';
          controlElements.camera.value = 'skyline';
        } else if (e.key === 'ArrowDown') {
          e.preventDefault();
          state.camera = 'rooftop';
          controlElements.camera.value = 'rooftop';
        } else if (e.key === 'ArrowLeft') {
          e.preventDefault();
          state.camera = 'birds-eye';
          controlElements.camera.value = 'birds-eye';
        } else if (e.key === 'ArrowRight') {
          e.preventDefault();
          state.scene = 'arrival';
          controlElements.scene.value = 'arrival';
        } else if (e.key === 'w') {
          e.preventDefault();
          state.density = Math.min(state.density + 0.1, 1);
          controlElements.density.value = state.density;
        } else if (e.key === 's') {
          e.preventDefault();
          state.density = Math.max(state.density - 0.1, 0.3);
          controlElements.density.value = state.density;
        } else if (e.key === 'r') {
          e.preventDefault();
          state.time = 0;
          state.frame = 0;
          initEntities();
        }
      });
    };

    window.addEventListener('resize', () => {
      canvas.width = window.innerWidth;
      canvas.height = window.innerHeight;
      width = canvas.width;
      height = canvas.height;
    });

    initEntities();
    bindControls();
    draw(0);
  </script>
</body>
</html>