• Updated 11 Nov, 12:43 UTC
  • Next run 12 Nov, 05:17 UTCNext 12 Nov 05:17
  • Last 7 days: 10 new models · 61 runs7 d: 10 models · 61 runs
Wristwatch that shows the current time

ricursiveAI/Ripple-65B-PreviewRicursiveai

Network offOpen alone (new tab)

Run details

Model
ricursiveai/ripple-65b-preview
Route
OpenRouter API, one user message
Served by
GMICloud
Effort
no thinking
Limits
60 min · 131,072 output tokens (the model's limit)
Date
7 Nov 2026
Time
29 s
Size
20 kB
Tokens
83 in, 6,236 out
List price
$0.004 at today's price ($0.20 in / $0.60 out per M)
Paid
$0.004

Raw files: output · meta.json

Exact prompt

Draw a mechanical wristwatch that shows the current local time, with a sweeping seconds hand and a date window. Make it look expensive.

It must be one complete, self-contained HTML file: inline all CSS and JavaScript, and make no network requests of any kind (no CDNs, web fonts or remote images). Reply with the file in a single ```html code block.

Source

20 kB 723 lines Raw

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Chronomaster — Mechanical Wristwatch</title>
<style>
  * {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
  }

  body {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    background:
      radial-gradient(circle at 35% 20%, #1b2436 0%, #0a0e16 45%, #05070b 100%);
    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
    overflow: hidden;
    position: relative;
  }

  body::before {
    content: "";
    position: fixed;
    inset: 0;
    background-image:
      linear-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px),
      linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px);
    background-size: 42px 42px;
    pointer-events: none;
  }

  #stage {
    position: relative;
    z-index: 1;
  }

  /* ---------- Lights that make it look expensive ---------- */
  body > .lamp {
    position: fixed;
    width: 400px;
    height: 400px;
    border-radius: 50%;
    filter: blur(80px);
    opacity: 0.55;
    pointer-events: none;
    z-index: 0;
  }
  body > .lamp:nth-child(1) {
    top: -120px;
    left: -140px;
    background: radial-gradient(circle, #8fd0ff 0%, rgba(140, 208, 255, 0) 70%);
    mix-blend-mode: screen;
  }
  body > .lamp:nth-child(2) {
    top: 60px;
    right: -90px;
    background: radial-gradient(circle, #ffc9a0 0%, rgba(255, 200, 160, 0) 70%);
    mix-blend-mode: screen;
    opacity: 0.4;
  }

  /* ---------- Outer lugs + strap ---------- */
  #watch {
    position: relative;
    width: 340px;
    aspect-ratio: 1 / 1;
    margin: 0 auto;
    transform-style: preserve-3d;
  }

  /* The metal case */
  #dial {
    width: 100%;
    aspect-ratio: 1 / 1;
    border-radius: 50%;
    background:
      radial-gradient(circle at 50% 28%, #2e3446 0%, #171b24 42%, #0b0d12 100%);
    border: 3px solid #c8ccd4;
    box-shadow:
      0 22px 45px rgba(0, 0, 0, 0.65),
      inset 0 1px 2px rgba(255, 255, 255, 0.18),
      inset 0 -18px 26px rgba(0, 0, 0, 0.65);
  }

  /* Rotating bezel */
  #bezel {
    position: absolute;
    inset: 7px;
    border-radius: 50%;
    pointer-events: none;
    background: conic-gradient(
      from 0deg,
      #1a1e2b 0deg,
      #262c3a 18deg,
      #6b727f 18deg,
      #8a9098 26deg,
      #2a2f3c 26deg,
      #1a1e2b 26deg,
      #262c3a 44deg,
      #dbe1ea 44deg,
      #eef2f7 46deg,
      #262c3a 46deg,
      #1a1e2b 64deg,
      #262c3a 64deg,
      #6b727f 82deg,
      #8a9098 90deg,
      #2a2f3c 90deg
    );
    border-radius: 50%;
    padding: 2px;
    background-size: 220px 220px;
    background-position: center;
    box-shadow:
      inset 0 0 10px rgba(0, 0, 0, 0.6),
      inset 0 -1px 0 rgba(255,255,255,0.25),
      inset 0 1px 0 rgba(0,0,0,0.4);
  }

  #bezel::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 50%;
    border: 2px solid #d8dde5;
    opacity: 0.9;
    box-shadow: 0 0 6px rgba(180, 190, 205, 0.35);
  }

  /* Snow-capped chapter ring scraped inside the bezel ticking marks */
  #bezel::before {
    content: "";
    position: absolute;
    inset: 4px;
    border-radius: 50%;
    background:
      repeating-radial-gradient(
        circle at center,
        transparent 0deg 15.5deg,
        rgba(255,255,255,0.55) 15.5deg 16.2deg
      );
    -webkit-mask-image: radial-gradient(circle, transparent 82%, #fff 83%);
    mask-image: radial-gradient(circle, transparent 82%, #fff 83%);
    pointer-events: none;
  }

  /* Inner chapter track */
  .track {
    position: absolute;
    inset: 12px;
    border-radius: 50%;
    pointer-events: none;
    background:
      radial-gradient(ellipse at center,
        rgba(0,0,0,0),
        #1b1f29 55%,
        #22272f 78%,
        #161a21 80%,
        #2d333d 88%,
        #232933 89%,
        #2b3038 89.8%,
        #10141b 90%,
        #3a414d 0
      );
    box-shadow:
      inset 0 0 14px rgba(0, 0, 0, 0.8),
      inset 0 0 2px rgba(255, 255, 255, 0.10);
  }

  /* Minute / hour markers */
  .marker {
    position: absolute;
    left: 50%;
    top: 50%;
    transform-origin: 50% 225px;
    width: 3px;
    background: linear-gradient(to bottom, #f2f5fa, #9aa3b4);
    border-radius: 2px;
  }

  .tick {
    position: absolute;
    left: 50%;
    top: 50%;
    transform-origin: 50% 225px;
    translate: -50% -50%;
    background: linear-gradient(to bottom, #cfd6e2, #5c6675);
    border-radius: 1px;
  }

  .tick.major { width: 2px; height: 8px; background: linear-gradient(to bottom, #ffffff, #8e98a8); }
  .tick.minor { width: 1px; height: 5px; opacity: 0.55; }
  .tick.quarter { width: 2px; height: 12px; background: linear-gradient(to bottom, #ffffff, #7f8a9a); }

  /* ---------- Hands hierarchy (long -> short so short render on top) ---------- */
  #sweepHour,
  #sweepMinute,
  #sweepSecond { transform-origin: 50% 178px; }
  #sweepSecond { z-index: 6; }
  #sweepMinute { z-index: 5; }
  #sweepHour   { z-index: 7; }

  #sweepSecond {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 1.4px;
    height: 128px;
    border-radius: 3px;
    background: linear-gradient(to top, #11161d 0%, #8e9aaf 45%, #e6ebf3 100%);
    box-shadow: 0 1px 2px rgba(0,0,0,0.6);
    will-change: transform;
  }
  #sweepSecond::after {
    content: "";
    position: absolute;
    left: 50%;
    bottom: -18px;
    width: 8px;
    height: 18px;
    translate: -50% 0;
    background: #d4daea;
    border-radius: 0 0 4px 4px;
    box-shadow: 0 2px 3px rgba(0,0,0,0.4);
  }

  #sweepMinute {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 4.2px;
    height: 100px;
    border-radius: 3px;
    background: linear-gradient(to top, #0f131a, #b9c2d0 60%, #f7fafd 100%);
    box-shadow: 0 2px 4px rgba(0,0,0,0.5);
    will-change: transform;
  }

  #sweepHour {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 6.5px;
    height: 56px;
    border-radius: 8px;
    background: linear-gradient(to top, #141a22, #9db0c7 55%, #f5f9ff 100%);
    box-shadow: 0 2px 5px rgba(0,0,0,0.55);
    will-change: transform;
  }

  #crownMarker {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 3px;
    height: 16px;
    border-radius: 2px;
    background: #8b94a6;
    opacity: .6;
  }

  /* ---------- Date window ---------- */
  #dateFrame {
    position: absolute;
    left: 50%;
    bottom: 62px;
    translate: -50% -50%;
    width: 76px;
    height: 34px;
    border-radius: 17px;
    overflow: hidden;
    box-shadow:
      0 4px 2px rgba(0,0,0,0.45),
      inset 0 0 14px rgba(0,0,0,0.7),
      inset 0 -3px 5px rgba(0,0,0,0.5);
    z-index: 8;
  }

  #dateFrame::after {
    content: "";
    position: absolute;
    inset: 2px;
    border-radius: 15px;
    background-image: radial-gradient(circle at 30% 30%, rgba(255,255,255,0.75) 1px, transparent 1.2px);
    background-size: 5px 5px;
    pointer-events: none;
    opacity: .55;
    z-index: 2;
  }

  #dateText {
    position: relative;
    z-index: 3;
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: "Courier New", Courier, monospace;
    font-size: 26px;
    font-weight: 600;
    letter-spacing: 0.04em;
    color: #0a0d14;
    text-shadow: 0 1px 0 #ffffff88, 0 -1px 0 #00000088;
    font-variant-numeric: tabular-nums;
  }

  /* ---------- Branding ---------- */
  #brand {
    position: absolute;
    left: 50%;
    top: 30px;
    translate: -50% -50%;
    font-family: "Arial Narrow", "Helvetica Neue", Arial, sans-serif;
    font-size: 21px;
    letter-spacing: 0.5em;
    text-indent: 0.5em;
    font-weight: 700;
    color: #ffffff;
    text-transform: uppercase;
    text-shadow: 0 1px 1px rgba(0,0,0,0.8), 0 0 22px rgba(245, 255, 255, 0.22);
    z-index: 8;
    pointer-events: none;
  }

  /* ---------- Rope lugs + strap ---------- */
  #lug {
    position: absolute;
    z-index: 4;
    width: 96px;
    height: 240px;
    border-radius: 48px / 120px;
    background: #b8beca;
    border: 2px solid #8d94a1;
    box-shadow: 0 2px 3px rgba(0,0,0,0.5);
  }
  #lug.left  { left: 0;  bottom: 18px; }
  #lug.right { right: 0; bottom: 18px; }

  #strap {
    position: absolute;
    left: 50%;
    bottom: 40px;
    translate: -50% 0;
    width: 72px;
    height: 42px;
    border-radius: 36px / 21px;
    background:
      repeating-linear-gradient(
        to bottom,
        #5c4f42 0px, #5c4f42 6px, #3a2e25 6px, #3a2e25 12px
      );
    box-shadow: 0 2px 4px rgba(0,0,0,0.6);
  }

  .corner {
    position: fixed;
    width: 150px;
    height: 150px;
    z-index: -1;
    pointer-events: none;
  }

  @media (max-width: 380px) {
    #sweepHour {
      height: 46px;
      border-radius: 6px;
    }
    #sweepMinute {
      height: 80px;
      width: 3.4px;
    }
    #sweepSecond {
      height: 104px;
    }
    #brand { font-size: 16px; letter-spacing: 0.36em; }
    #dateText { font-size: 19px; }
    #dateFrame { width: 60px; height: 28px; border-radius: 14px; }
    #dateFrame::after { background-size: 4px 4px; }
  }
</style>
</head>
<body>

<div class="lamp"></div>
<div class="lamp"></div>

<div id="stage">
  <div id="watch">

    <!-- Lug tops -->
    <div id="lug" class="left"></div>
    <div id="lug" class="right"></div>

    <!-- Main case -->
    <div id="dial">

      <!-- Bezel over chapter ring -->
      <div id="bezel"></div>

      <!-- Inner tracks -->
      <div class="track"></div>

      <!-- Dot marker -->
      <div id="crownMarker" style="top: 45%;"></div>
      <div id="crownMarker" style="top: 55%; transform: rotate(180deg);"></div>

      <!-- Hour ticks -->
      <div id="tickRay" style="position:absolute; inset:10px; display:none;"></div>

      #ticks

      <!-- Hands -->
      <div id="sweepSecond"></div>
      <div id="sweepMinute"></div>
      <div id="sweepHour"></div>

      <!-- Brand -->
      <div id="brand">Chronomaster</div>

      <!-- Date aperture -->
      <div id="dateFrame">
        <div id="dateText">01</div>
      </div>
    </div>

    <!-- Bottom strap start -->
    <div id="strap"></div>
  </div>
</div>

<script>
(function () {
  "use strict";

  const dateFrame = document.getElementById("dateText");
  const tickRay   = document.getElementById("tickRay");
  const handSec   = document.getElementById("sweepSecond");
  const handMin   = document.getElementById("sweepMinute");
  const handHour  = document.getElementById("sweepHour");

  /* --------------------------------------------------------- *
   * 1. Build the chapter ring: ticks + Arabic numerals
   * --------------------------------------------------------- */
  function drawChapterRing() {
    const ring = document.getElementById("tickRay");
    // numerals overlay, absolutely centered by its own coordinate
    const numerals = document.getElementById("numerals");
    const NS = "http://www.w3.org/2000/svg";

    let majorCount = 0;
    for (let i = 0; i < 60; i++) {
      const angle = i * 6;                       // degrees around dial
      const big   = (i % 5 === 0) ? true : false;
      const tiny  = (i % 15 === 0) ? true : false;
      const cls   = big ? (tiny ? "tick quarter" : "tick major") : "tick minor";

      const el = document.createElement("div");
      el.className = "tick " + cls;
      el.style.width = big ? "3px" : "1px";
      el.style.height = big ? "13px" : (tiny ? "A" : "6px");
      el.style.transform = "rotate(" + angle + "deg) translate(0,-170px) rotate(-" + angle + "deg)";
      // Height of ticks: we place them at radius via top: 50% and transform-origin
      el.style.transformOrigin = "50% 170px";

      if (big) {
        // Giving explicit dimensions per class
        el.style.height = tiny ? "15px" : "11px";
        el.style.width = tiny ? "3px" : "2px";
      }

      ring.appendChild(el);
      if (big) majorCount++;

      // Arabic numeral for each 5-minute mark
      if (i % 5 === 0) {
        const n = (i === 0) ? 12 : (i / 5);
        const nEl = document.createElementNS(NS, "text");
        nEl.setAttribute("text-anchor", "middle");
        nEl.setAttribute("dominant-baseline", "central");
        nEl.setAttribute("y", 0);
        nEl.setAttribute("font-family", "Helvetica Neue, Helvetica, Arial, sans-serif");
        nEl.setAttribute("font-size", "11");
        nEl.setAttribute("font-weight", "600");
        nEl.setAttribute("fill", "#8d97a9");
        nEl.textContent = String(n);

        const wrap = document.createElementNS(NS, "g");
        wrap.setAttribute("transform",
          "rotate(" + angle.toFixed(2) + " 170 170) translate(0,-132)");
        wrap.appendChild(nEl);
        numerals.appendChild(wrap);
      }
    }
  }

  // Create numerals group before drawing
  (function makeNumerals() {
    const NS = "http://www.w3.org/2000/svg";
    const g  = document.createElementNS(NS, "g");
    const s  = document.createElementNS(NS, "svg");
    s.setAttribute("width", "340");
    s.setAttribute("height", "340");
    s.setAttribute("x", "0");
    s.setAttribute("y", "0");
    s.setAttribute("overflow", "visible");
    g.id = "numerals";
    s.appendChild(g);
    // visitors: we'll just add a container div for the group
    const holder = document.getElementById("numerals");
    // Avoid conflicts: use a plain div for numerals, positioned center
    const d = document.createElement("div");
    d.id = "numerals";
    d.style.cssText =
      "position:absolute;left:50%;top:50%;width:340px;height:340px;transform:translate(-50%,-50%);" +
      "z-index:2;pointer-events:none;";
    document.getElementById("dial").appendChild(d);
    // Render numerals as HTML spans instead of SVG for simplicity
    const spans = document.querySelectorAll("#numerals span.n");
    for (const span of Array.from(spans)) {
      const deg = parseFloat(span.getAttribute("data-d"));
      span.style.transform = "rotate(" + deg + "deg) translate(0px,-132px) rotate(-" + deg + "deg)";
      span.style.transformOrigin = "170px 170px";
    }
    holder.removeAllListeners && 0;
  })();

  /* --------------------------------------------------------- *
   * 2. Tick generation (pure DOM)
   * --------------------------------------------------------- */
  function buildTicks() {
    const ring = document.getElementById("tickRay");
    // Keep the container for transforms only
    ring.style.display = "block";
    const base = document.createElement("div");
    base.className = "ticksBase";
    // We'll just place ticking marks directly onto it.
    for (let i = 0; i < 60; i++) {
      const angle = i * 6;
      const big   = (i % 5 === 0);
      const tiny  = (i % 15 === 0);

      const el = document.createElement("div");
      el.className = "tick";
      el.style.transformOrigin = "50% 170px";
      el.style.left = "50%";
      el.style.top  = "50%";
      el.style.transform = "rotate(" + angle + "deg)";

      if (big) {
        el.style.backgroundColor = tiny ? "#f4f8ff" : "#c3ccd8";
        el.style.width  = tiny ? "3px" : "2px";
        el.style.height = tiny ? "15px" : "11px";
        el.style.boxShadow = "0 0 2px #6b7485";
      } else {
        el.style.background = "#c3ccd8";
        el.style.width  = "1px";
        el.style.height = "6px";
        el.style.opacity = 0.75;
      }

      base.appendChild(el);

      // Arabic numeral
      if (big) {
        const n = (i === 0) ? 12 : (i / 5);
        const span = document.createElement("span");
        span.className = "n";
        span.textContent = String(n);
        span.style.display = "block";
        span.style.position = "absolute";
        span.style.left = "50%";
        span.style.top  = "50%";
        span.style """
      }
    }
  }
  // Remove broken leftover, replaced below for reliability
  document.getElementById("tickRay").innerHTML = "";

  (function renderFace() {
    const outer = document.createElement("div");
    outer.className = "faceTicks";
    outer.style.cssText =
      "position:absolute;left:0;top:0;width:340px;height:340px;" +
      "border-radius:50%;overflow:hidden;pointer-events:none;z-index:1;";

    for (let i = 0; i < 60; i++) {
      const angle = i * 6;
      const isBig = (i % 5 === 0);
      const isQuart = (i % 15 === 0);

      // Tick mark
      const tick = document.createElement("div");
      tick.className = "tick";
      tick.style.transformOrigin = "50% 170px";
      tick.style.left = "50%";
      tick.style.top  = "50%";
      tick.style.transform = "rotate(" + angle + "deg) translateY(-70%)";

      if (isBig) {
        tick.style.transform = "rotate(" + angle + "deg)";
        tick.style.width  = isQuart ? "3px" : "2px";
        tick.style.height = isQuart ? "17px" : "12px";
        tick.style.background = "linear-gradient(to bottom, #ffffff, #9aa4b6)";
        tick.style.boxShadow = "0 0 2px #5e6878";
      } else {
        tick.style.width  = "1px";
        tick.style.height = "6px";
        tick.style.background = "linear-gradient(to bottom, #c3ccd8, #6d7788)";
        tick.style.opacity = 0.7;
      }

      outer.appendChild(tick);
    }

    // Arabic numerals
    const nums = document.createElement("div");
    nums.className = "numerals";
    nums.style.cssText =
      "position:absolute;left:50%;top:50%;width:340px;height:340px;" +
      "transform:translate(-50%,-50%);pointer-events:none;z-index:3;";

    for (let i = 0; i < 12; i++) {
      const label = (i === 0) ? 12 : i;
      const angle = i * 30;
      const span = nums.jQuery ? null : null;

      const s = document.createElement("span");
      s.className = "n";
      s.textContent = String(label);
      s.style.position = "absolute";
      s.style.left = "50%";
      s.style.top  = "50%";
      s.style.whiteSpace = "nowrap";
      s.style	fontSize = "11px";
      s.style.fontWeight = "600";
      s.style.color = "#7b8699";
      s.style.fontFamily = "Helvetica Neue, Helvetica, Arial, sans-serif";
      s.style.transform =
        "rotate(" + angle + "deg) rotate(-" + angle + "deg) translateY(-104px)";
      s.style.transformOrigin = "170px 170px";
      nums.appendChild(s);
    }

    document.getElementById("dial").appendChild(outer);
  })();

  /* --------------------------------------------------------- *
   * 3. State + current time sync
   * --------------------------------------------------------- */
  let msOffset = 0;
  let lastTs   = 0;
  let running  = true;

  function nowParts() {
    const d = new Date();
    // msOffset compensates for timer drift; add smoothing
    const ms = d.getTime() + msOffset;
    const d2 = new Date(ms);
    return {
      ms: ms,
      hours: d2.getHours() % 12,
      minutes: d2.getMinutes(),
      seconds: d2.getSeconds(),
      dSec: (d2.getMilliseconds() / 1000) % 10
    };
  }

  function setDateWindow(dateObj) {
    const d = dateObj || new Date();
    const day = String(d.getDate());
    dateFrame.textContent = day < 10 ? "0" + day : day;
  }

  function applyCurrent() {
    const p = nowParts();
    const hourAngle   = (p.hours + p.minutes / 60) * 30;
    const minuteAngle = (p.minutes + p.seconds / 60) * 6;
    const secondAngle = (p.seconds + p.dSec) * 6;

    handHour.style.transform   = "rotate(" + hourAngle.toFixed(4) + "deg)";
    handMin.style.transform    = "rotate(" + minuteAngle.toFixed(4) + "deg)";
    handSec.style.transform    = "rotate(" + secondAngle.toFixed(4) + "deg)";

    setDateWindow(new Date());
  }

  // Align to next second boundary for smooth sweeping
  let rafId = null;

  function tick(ts) {
    if (!lastTs) lastTs = ts;
    msOffset += ts - lastTs;   // keep absolute drift low
    lastTs = ts;

    applyCurrent();

    rafId = requestAnimationFrame(tick);
  }

  /* --------------------------------------------------------- *
   * 4. Gentle ambient animation on the case (only CSS variables)
   * --------------------------------------------------------- */
  let ambient = 0;
  setInterval(function () {
    ambient = (ambient + 1) % 100;
    document.documentElement.style.setProperty("--glow", ambient);
  }, 250);

  /* --------------------------------------------------------- *
   * 5. Start
   * --------------------------------------------------------- */
  setDateWindow(new Date());
  requestAnimationFrame(tick);
})();
</script>
</body>
</html>

Compare with

ricursiveAI/Ripple-65B-Preview: other attempts