{
  "slug": "market-ticker",
  "title": "Market ticker strip",
  "tagline": "Seamless scrolling ticker of symbols with logo, price and change — stocks, crypto or anything with a number. 1920×160 strip, 10 s loop.",
  "description": "Free CC0 market ticker motion template: scrolling strip with logos from gimgs.net (stock, crypto, brand), price and coloured change. Self-contained HTML, loops seamlessly, agent-friendly.",
  "category": "ticker",
  "tags": [
    "ticker",
    "stocks",
    "crypto",
    "finance",
    "loop"
  ],
  "tier": "free",
  "license": "CC0",
  "spec": {
    "width": 1920,
    "height": 160,
    "fps": 30,
    "duration": 10,
    "aspect": "12:1"
  },
  "kind": "html",
  "params_schema": {
    "type": "object",
    "properties": {
      "items": {
        "type": "array",
        "description": "Ticker items in order. Each: {symbol, name, price, change, logo}. change starting with '-' is red. logo: https://gimgs.net/stock/{tradingview-slug}.svg, /crypto/{ticker}.svg or /brand-logo/{slug}.svg",
        "default": [
          {
            "symbol": "BTC",
            "name": "Bitcoin",
            "price": "$112,430",
            "change": "+2.4%",
            "logo": "https://gimgs.net/crypto/btc.svg"
          },
          {
            "symbol": "ETH",
            "name": "Ethereum",
            "price": "$4,120",
            "change": "-0.8%",
            "logo": "https://gimgs.net/crypto/eth.svg"
          },
          {
            "symbol": "AAPL",
            "name": "Apple",
            "price": "$248.10",
            "change": "+1.1%",
            "logo": "https://gimgs.net/stock/apple.svg"
          },
          {
            "symbol": "NVDA",
            "name": "NVIDIA",
            "price": "$181.20",
            "change": "+3.6%",
            "logo": "https://gimgs.net/stock/nvidia.svg"
          },
          {
            "symbol": "TSLA",
            "name": "Tesla",
            "price": "$412.00",
            "change": "-1.9%",
            "logo": "https://gimgs.net/stock/tesla.svg"
          }
        ]
      },
      "bg": {
        "type": "string",
        "format": "color",
        "default": "#0b0c10",
        "description": "Strip background"
      },
      "up": {
        "type": "string",
        "format": "color",
        "default": "#22c55e",
        "description": "Positive change colour"
      },
      "down": {
        "type": "string",
        "format": "color",
        "default": "#ef4444",
        "description": "Negative change colour"
      }
    }
  },
  "default_params": {
    "items": [
      {
        "symbol": "BTC",
        "name": "Bitcoin",
        "price": "$112,430",
        "change": "+2.4%",
        "logo": "https://gimgs.net/crypto/btc.svg"
      },
      {
        "symbol": "ETH",
        "name": "Ethereum",
        "price": "$4,120",
        "change": "-0.8%",
        "logo": "https://gimgs.net/crypto/eth.svg"
      },
      {
        "symbol": "AAPL",
        "name": "Apple",
        "price": "$248.10",
        "change": "+1.1%",
        "logo": "https://gimgs.net/stock/apple.svg"
      },
      {
        "symbol": "NVDA",
        "name": "NVIDIA",
        "price": "$181.20",
        "change": "+3.6%",
        "logo": "https://gimgs.net/stock/nvidia.svg"
      },
      {
        "symbol": "TSLA",
        "name": "Tesla",
        "price": "$412.00",
        "change": "-1.9%",
        "logo": "https://gimgs.net/stock/tesla.svg"
      }
    ],
    "bg": "#0b0c10",
    "up": "#22c55e",
    "down": "#ef4444"
  },
  "preview": {
    "poster": "https://gimgs.net/motion/media/market-ticker/poster-030da6fd1bc9.jpg",
    "video": ""
  },
  "variants": [],
  "credits": {
    "author": "gimgs",
    "url": "https://gimgs.net/motion"
  },
  "page_url": "https://gimgs.net/motion/market-ticker.html",
  "json_url": "https://gimgs.net/motion/market-ticker.json",
  "template_url": "https://gimgs.net/motion/market-ticker/template.html",
  "play_url": "https://gimgs.net/motion/market-ticker/play",
  "markdown_url": "https://gimgs.net/motion/market-ticker.md",
  "updated_at": "2026-09-24T07:24:43.566Z",
  "prompt": "## When to use\nA finance/news strip at the bottom of a video, a looping background for a market update, a dashboard header. The output is a 1920×160 strip: overlay it on 1080p footage with ffmpeg `overlay=0:920`.\n\n## How to adapt\n- Fill `items` with real data — prices and changes are plain strings so format them however the audience expects (`€1.234,56`, `+0.8 %`, `▲ 2.4%`). Anything whose `change` starts with `-` is coloured `down`.\n- Logos: stock → `https://gimgs.net/stock/{tradingview slug}.svg` (apple, microsoft, nvidia…; use `/api/stock-search?q=` to find the slug), crypto → `/crypto/{ticker}.svg`, brands → `/brand-logo/{slug}.svg`. Omit `logo` to hide the circle.\n- 5–8 items fill the width nicely; the list is duplicated internally so the loop is seamless at any count.\n- Speed: the rail travels half its width in `duration` (10 s). More items = faster. Change `window.motion.duration` in the source for a slower loop.\n\n## Do not change\n`window.motion`, `#stage` size, the rail's `translateX(-50%)` loop (that is what makes frame 0 and the last frame line up).",
  "flow": [
    {
      "step": "Collect quotes",
      "tool": "any market data API",
      "note": "Format numbers as strings."
    },
    {
      "step": "Resolve logos",
      "tool": "gimgs /api/stock-search?q=apple → logoid",
      "note": "→ https://gimgs.net/stock/{logoid}.svg"
    },
    {
      "step": "Render loop",
      "cmd": "frames 0…299 at t=i/30 → ffmpeg -framerate 30 -i frame-%04d.png -c:v libx264 -pix_fmt yuv420p ticker.mp4"
    },
    {
      "step": "Overlay",
      "cmd": "ffmpeg -i video.mp4 -stream_loop -1 -i ticker.mp4 -filter_complex \"[1]format=yuva420p[t];[0][t]overlay=0:920:shortest=1\" out.mp4"
    }
  ],
  "files": [],
  "contract": "Self-contained HTML. Declares window.motion = { width, height, fps, duration, params, apply(params) [, seek(t)] }; root element #stage sized width×height; animations are CSS/WAAPI with animation-fill-mode: both so any t can be seeked via document.getAnimations(). The host runtime (injected by /play) merges ?p params, calls apply(), scales #stage to the viewport and, when ?t is given, pauses and seeks deterministically.",
  "render": {
    "frames": 300,
    "frame_url": "https://gimgs.net/motion/market-ticker/play?p=<base64url(JSON params)>&t=<seconds>",
    "note": "Open frame_url in a headless browser with viewport width×height (deviceScaleFactor 1), wait for document.documentElement.dataset.motionReady === '1', screenshot; repeat for t = i/fps (i = 0…frames-1); then `ffmpeg -framerate <fps> -i frame-%04d.png -c:v libx264 -pix_fmt yuv420p out.mp4`. Or drop ?t to play it live."
  },
  "source": "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Market ticker strip</title>\n<style>\n  @font-face{font-family:'IBM Plex Mono';font-weight:400 500;font-display:block;src:url(https://gimgs.net/fonts/plexmono-500-latin.woff2) format('woff2')}\n  @font-face{font-family:'IBM Plex Sans';font-weight:400 600;font-display:block;src:url(https://gimgs.net/fonts/plexsans-400-latin.woff2) format('woff2')}\n  html,body{margin:0;background:#000}\n  #stage{position:relative;width:1920px;height:160px;overflow:hidden;background:var(--bg,#0b0c10);color:#fff;font-family:'IBM Plex Sans',system-ui,sans-serif}\n  .rail{position:absolute;top:0;left:0;height:100%;display:flex;align-items:center;white-space:nowrap;\n    animation:scroll var(--dur,10s) linear both}\n  @keyframes scroll{from{transform:translateX(0)}to{transform:translateX(-50%)}}\n  .it{display:inline-flex;align-items:center;gap:22px;padding:0 48px;height:100%;border-right:1px solid rgba(255,255,255,.08)}\n  .it img{width:64px;height:64px;border-radius:50%;background:#fff;object-fit:contain;padding:6px;box-sizing:border-box}\n  .sym{font-family:'IBM Plex Mono',ui-monospace,monospace;font-weight:500;font-size:40px;letter-spacing:.02em}\n  .nm{font-size:22px;color:rgba(255,255,255,.55);margin-top:2px}\n  .px{font-family:'IBM Plex Mono',ui-monospace,monospace;font-size:40px;margin-left:12px}\n  .chg{font-family:'IBM Plex Mono',ui-monospace,monospace;font-size:28px;padding:6px 14px;border-radius:8px;font-weight:500}\n  .up{color:var(--up,#22c55e);background:color-mix(in srgb,var(--up,#22c55e) 16%,transparent)}\n  .dn{color:var(--dn,#ef4444);background:color-mix(in srgb,var(--dn,#ef4444) 16%,transparent)}\n  .fade{position:absolute;top:0;bottom:0;width:180px;pointer-events:none}\n  .fade.l{left:0;background:linear-gradient(90deg,var(--bg,#0b0c10),transparent)}\n  .fade.r{right:0;background:linear-gradient(-90deg,var(--bg,#0b0c10),transparent)}\n</style>\n</head>\n<body>\n<div id=\"stage\">\n  <div class=\"rail\" id=\"rail\"></div>\n  <div class=\"fade l\"></div><div class=\"fade r\"></div>\n</div>\n<script>\nwindow.motion = {\n  width: 1920, height: 160, fps: 30, duration: 10,\n  params: {\n    items: [\n      { symbol: \"BTC\", name: \"Bitcoin\", price: \"$112,430\", change: \"+2.4%\", logo: \"https://gimgs.net/crypto/btc.svg\" },\n      { symbol: \"ETH\", name: \"Ethereum\", price: \"$4,120\", change: \"-0.8%\", logo: \"https://gimgs.net/crypto/eth.svg\" },\n      { symbol: \"AAPL\", name: \"Apple\", price: \"$248.10\", change: \"+1.1%\", logo: \"https://gimgs.net/stock/apple.svg\" },\n      { symbol: \"NVDA\", name: \"NVIDIA\", price: \"$181.20\", change: \"+3.6%\", logo: \"https://gimgs.net/stock/nvidia.svg\" },\n      { symbol: \"TSLA\", name: \"Tesla\", price: \"$412.00\", change: \"-1.9%\", logo: \"https://gimgs.net/stock/tesla.svg\" }\n    ],\n    bg: \"#0b0c10\", up: \"#22c55e\", down: \"#ef4444\"\n  },\n  apply: function (p) {\n    var s = document.getElementById(\"stage\");\n    s.style.setProperty(\"--bg\", p.bg); s.style.setProperty(\"--up\", p.up); s.style.setProperty(\"--dn\", p.down);\n    var rail = document.getElementById(\"rail\");\n    while (rail.firstChild) rail.removeChild(rail.firstChild);\n    var items = Array.isArray(p.items) ? p.items : [];\n    function el(tag, cls, text) { var e = document.createElement(tag); if (cls) e.className = cls; if (text != null) e.textContent = text; return e; }\n    // the list is rendered twice so a translateX(-50%) loop is seamless\n    for (var r = 0; r < 2; r++) items.forEach(function (it) {\n      var d = el(\"div\", \"it\");\n      if (it.logo) { var img = el(\"img\"); img.src = it.logo; img.alt = \"\"; d.appendChild(img); }\n      var t = el(\"div\"); t.appendChild(el(\"div\", \"sym\", it.symbol || \"\")); t.appendChild(el(\"div\", \"nm\", it.name || \"\")); d.appendChild(t);\n      d.appendChild(el(\"div\", \"px\", it.price || \"\"));\n      var c = String(it.change || \"\"); d.appendChild(el(\"div\", \"chg \" + (c.trim().charAt(0) === \"-\" ? \"dn\" : \"up\"), c));\n      rail.appendChild(d);\n    });\n    s.style.setProperty(\"--dur\", window.motion.duration + \"s\");\n  }\n};\n</script>\n</body>\n</html>\n"
}