{"id":16766,"date":"2026-07-01T08:35:45","date_gmt":"2026-07-01T08:35:45","guid":{"rendered":"https:\/\/theandroidapk.com\/blog\/?p=16766"},"modified":"2026-07-01T10:04:05","modified_gmt":"2026-07-01T10:04:05","slug":"vivid2201-dodge-the-swarm-top-the-board","status":"publish","type":"post","link":"https:\/\/theandroidapk.com\/blog\/vivid2201-dodge-the-swarm-top-the-board\/","title":{"rendered":"Vivid2201: Dodge the Swarm, Top the Board"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-post\" data-elementor-id=\"16766\" class=\"elementor elementor-16766\">\n\t\t\t\t<div class=\"elementor-element elementor-element-f78a57e e-flex e-con-boxed e-con e-parent\" data-id=\"f78a57e\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-4029b97 elementor-widget elementor-widget-html\" data-id=\"4029b97\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!-- ===================================================================\r\n     VIVID2201 - paste this WHOLE block into ONE \"Custom HTML\" block at the\r\n     top of your WordPress post. Then write your article below it in normal\r\n     blocks. The styles are locked to the game (everything is under .v2201),\r\n     so this will NOT change your page background, text, or layout.\r\n     =================================================================== -->\r\n<style>\r\n\/* =========================================================================\r\n   VIVID2201 - scoped styles\r\n   Everything is prefixed \"v2201-\" and lives under .v2201, so this file will\r\n   not touch your theme and your theme will not leak into the game.\r\n   To blend with your page instead of the dark backdrop, set\r\n   .v2201 { background: transparent; }\r\n   ========================================================================= *\/\r\n\r\n.v2201{\r\n  \/* palette (scoped variables) *\/\r\n  --v2201-ink:#eaf2ff;\r\n  --v2201-dim:#93a4c6;\r\n  --v2201-cyan:#35e0ff;\r\n  --v2201-magenta:#b23bff;\r\n  --v2201-amber:#ffb020;\r\n  --v2201-danger:#ff5d6c;\r\n  --v2201-panel:rgba(9,13,27,.86);\r\n  --v2201-line:rgba(53,224,255,.28);\r\n  --v2201-bg0:#04050c;\r\n\r\n  \/* self-contained baseline so the host theme cannot leak in *\/\r\n  display:flex;\r\n  justify-content:center;\r\n  align-items:flex-start;\r\n  width:100%;\r\n  padding:14px;\r\n  font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,Arial,sans-serif;\r\n  font-size:16px;\r\n  font-weight:400;\r\n  font-style:normal;\r\n  line-height:1.5;\r\n  letter-spacing:normal;\r\n  text-transform:none;\r\n  color:var(--v2201-ink);\r\n  background:\r\n    radial-gradient(1200px 700px at 70% -10%, #16123a 0%, transparent 60%),\r\n    radial-gradient(900px 600px at 0% 110%, #0b2340 0%, transparent 55%),\r\n    var(--v2201-bg0);\r\n  border-radius:18px;\r\n}\r\n\r\n\/* resets (kept at zero specificity with :where so they never beat components) *\/\r\n.v2201, .v2201 *{ box-sizing:border-box; -webkit-tap-highlight-color:transparent; }\r\n.v2201 :where(p,h1,h2,h3,h4,div,section,span,ul,ol,li){\r\n  margin:0; padding:0; border:0; background:none;\r\n  color:inherit; font-family:inherit; font-size:inherit; font-weight:inherit;\r\n  font-style:normal; line-height:inherit; letter-spacing:inherit;\r\n  text-transform:none; text-align:inherit;\r\n}\r\n.v2201 :where(button,input){ margin:0; font-family:inherit; -webkit-appearance:none; appearance:none; }\r\n\r\n.v2201-wrap{\r\n  position:relative;width:100%;max-width:940px;aspect-ratio:900\/560;\r\n  border-radius:18px;overflow:hidden;\r\n  box-shadow:0 30px 80px -30px rgba(0,0,0,.85), 0 0 0 1px rgba(53,224,255,.1) inset;\r\n  touch-action:none;\r\n}\r\n.v2201-canvas{display:block;width:100%;height:100%;image-rendering:auto;}\r\n\r\n\/* Overlays *\/\r\n.v2201-screen{\r\n  position:absolute;inset:0;display:none;flex-direction:column;\r\n  align-items:center;justify-content:center;gap:16px;\r\n  padding:22px;text-align:center;\r\n  background:radial-gradient(700px 500px at 50% 30%, rgba(9,13,30,.55), rgba(4,5,12,.86));\r\n  -webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);\r\n}\r\n.v2201-screen.v2201-shown{display:flex;}\r\n\r\n.v2201-title{\r\n  font-weight:900;letter-spacing:.14em;line-height:.92;\r\n  font-size:clamp(30px,7vw,60px);\r\n  text-transform:uppercase;\r\n  background:linear-gradient(180deg,#ffffff, var(--v2201-cyan));\r\n  -webkit-background-clip:text;background-clip:text;color:transparent;\r\n  text-shadow:0 0 30px rgba(53,224,255,.25);\r\n}\r\n.v2201-subtitle{color:var(--v2201-dim);font-size:clamp(12px,2.4vw,15px);letter-spacing:.05em;max-width:34ch;}\r\n.v2201-accent{width:64px;height:3px;border-radius:3px;background:linear-gradient(90deg,var(--v2201-cyan),var(--v2201-magenta));box-shadow:0 0 16px var(--v2201-cyan);}\r\n\r\n.v2201-howto{color:var(--v2201-dim);font-size:clamp(11px,2.2vw,13.5px);line-height:1.5;max-width:40ch;}\r\n.v2201-howto b{color:var(--v2201-ink);font-weight:700;}\r\n\r\n.v2201-row{display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center;}\r\n.v2201-name{\r\n  background:rgba(3,6,16,.9);border:1px solid var(--v2201-line);color:var(--v2201-ink);\r\n  padding:12px 14px;border-radius:12px;font-size:15px;letter-spacing:.08em;\r\n  width:min(240px,60vw);text-transform:uppercase;outline:none;font-weight:700;\r\n}\r\n.v2201-name::placeholder{color:#5f6f92;letter-spacing:.12em;}\r\n.v2201-name:focus{border-color:var(--v2201-cyan);box-shadow:0 0 0 3px rgba(53,224,255,.18);}\r\n\r\n.v2201-btn{\r\n  cursor:pointer;border:0;border-radius:12px;font-weight:800;letter-spacing:.1em;\r\n  text-transform:uppercase;font-size:15px;padding:13px 22px;color:#03121a;\r\n  background:linear-gradient(180deg,#7ff0ff,var(--v2201-cyan));\r\n  box-shadow:0 10px 28px -10px rgba(53,224,255,.7), 0 0 0 1px rgba(255,255,255,.25) inset;\r\n  transition:transform .08s ease, filter .15s ease;\r\n}\r\n.v2201-btn:hover{transform:translateY(-2px);filter:brightness(1.06);}\r\n.v2201-btn:active{transform:translateY(0);}\r\n.v2201-btn.v2201-alt{background:linear-gradient(180deg,#d99bff,var(--v2201-magenta));color:#160121;box-shadow:0 10px 28px -10px rgba(178,59,255,.7), 0 0 0 1px rgba(255,255,255,.2) inset;}\r\n\r\n\/* Leaderboard *\/\r\n.v2201-board{width:min(420px,88vw);background:var(--v2201-panel);border:1px solid var(--v2201-line);border-radius:14px;padding:12px 12px 10px;}\r\n.v2201-board-title{font-size:12px;letter-spacing:.22em;color:var(--v2201-cyan);text-transform:uppercase;font-weight:800;display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;}\r\n.v2201-tag{color:var(--v2201-dim);font-weight:600;letter-spacing:.08em;font-size:10px;}\r\n.v2201-list{display:flex;flex-direction:column;gap:3px;max-height:34vh;overflow:auto;}\r\n.v2201-lb-row{display:grid;grid-template-columns:34px 1fr auto;align-items:center;gap:10px;padding:7px 9px;border-radius:9px;background:rgba(255,255,255,.02);font-size:13.5px;}\r\n.v2201-lb-row .v2201-rk{font-variant-numeric:tabular-nums;font-weight:800;color:var(--v2201-dim);text-align:center;}\r\n.v2201-lb-row .v2201-nm{font-weight:600;letter-spacing:.04em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-transform:uppercase;}\r\n.v2201-lb-row .v2201-sc{font-variant-numeric:tabular-nums;font-weight:800;color:var(--v2201-ink);}\r\n.v2201-lb-row.v2201-gold .v2201-rk{color:#ffd54a;}   .v2201-lb-row.v2201-gold{background:rgba(255,213,74,.08);}\r\n.v2201-lb-row.v2201-silver .v2201-rk{color:#dce3ee;} .v2201-lb-row.v2201-silver{background:rgba(220,227,238,.06);}\r\n.v2201-lb-row.v2201-bronze .v2201-rk{color:#e39b63;} .v2201-lb-row.v2201-bronze{background:rgba(227,155,99,.07);}\r\n.v2201-lb-row.v2201-me{outline:1px solid var(--v2201-cyan);background:rgba(53,224,255,.1);}\r\n.v2201-lb-row.v2201-me .v2201-sc{color:var(--v2201-cyan);}\r\n.v2201-empty{color:var(--v2201-dim);font-size:12.5px;padding:10px 4px;}\r\n.v2201-sep{height:1px;background:var(--v2201-line);margin:4px 6px;opacity:.6;}\r\n\r\n.v2201-note{color:#6b7ba0;font-size:10.5px;letter-spacing:.03em;margin-top:2px;}\r\n\r\n\/* Game over *\/\r\n.v2201-final{display:flex;flex-direction:column;align-items:center;gap:2px;}\r\n.v2201-final-label{color:var(--v2201-dim);font-size:11px;letter-spacing:.28em;text-transform:uppercase;}\r\n.v2201-final-score{font-size:clamp(40px,10vw,68px);font-weight:900;line-height:1;font-variant-numeric:tabular-nums;text-shadow:0 0 24px rgba(53,224,255,.3);}\r\n.v2201-badge{display:inline-block;margin-top:2px;padding:4px 12px;border-radius:999px;font-size:11px;font-weight:800;letter-spacing:.14em;text-transform:uppercase;\r\n  background:linear-gradient(90deg,var(--v2201-amber),#ff7a2c);color:#231000;box-shadow:0 0 18px rgba(255,176,32,.5);}\r\n.v2201-gameover{font-weight:900;letter-spacing:.16em;font-size:clamp(20px,5vw,30px);text-transform:uppercase;color:var(--v2201-danger);text-shadow:0 0 24px rgba(255,93,108,.35);}\r\n\r\n\/* Mute *\/\r\n.v2201-mute{position:absolute;top:12px;right:12px;z-index:5;width:40px;height:40px;border-radius:11px;border:1px solid var(--v2201-line);\r\n  background:rgba(4,7,16,.6);color:var(--v2201-ink);cursor:pointer;font-size:17px;display:flex;align-items:center;justify-content:center;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);}\r\n.v2201-mute:hover{border-color:var(--v2201-cyan);}\r\n\r\n@media (max-width:560px){\r\n  .v2201-board{max-height:none;}\r\n  .v2201-list{max-height:28vh;}\r\n}\r\n\r\n<\/style>\r\n\r\n<!-- ================= VIVID2201 game markup =================\r\n     Paste this where you want the game to appear on the page.\r\n     It has no ids and no heading tags, so it will not clash with\r\n     your theme or affect your page's heading \/ SEO structure.\r\n     Load vivid2201.css and vivid2201.js for it to work.\r\n     ========================================================= -->\r\n<div class=\"v2201\">\r\n  <div class=\"v2201-wrap\">\r\n    <canvas class=\"v2201-canvas\"><\/canvas>\r\n    <button class=\"v2201-mute\" type=\"button\" title=\"Sound on\/off\" aria-label=\"Toggle sound\">&#128266;<\/button>\r\n\r\n    <!-- Start screen -->\r\n    <section class=\"v2201-screen v2201-screen-start v2201-shown\">\r\n      <div class=\"v2201-title\">Vivid2201<\/div>\r\n      <div class=\"v2201-accent\"><\/div>\r\n      <p class=\"v2201-subtitle\">Alien Gauntlet - thread your fighter through the swarm and rack up the score.<\/p>\r\n      <p class=\"v2201-howto\"><b>Tap, click, or press Space<\/b> to lift the jet. Gravity pulls it down, so keep tapping. Dodge every alien ship, <b>+5 points<\/b> each. Do not hit the ground. Every 20 ships you pass, the swarm speeds up.<\/p>\r\n      <div class=\"v2201-row\">\r\n        <input class=\"v2201-name\" maxlength=\"14\" placeholder=\"Pilot name\" autocomplete=\"off\" spellcheck=\"false\">\r\n        <button class=\"v2201-btn v2201-btn-start\" type=\"button\">Start run<\/button>\r\n      <\/div>\r\n      <div class=\"v2201-board\">\r\n        <div class=\"v2201-board-title\">Top Pilots <span class=\"v2201-tag\">world board<\/span><\/div>\r\n        <div class=\"v2201-list v2201-list-start\"><div class=\"v2201-empty\">Loading scores...<\/div><\/div>\r\n      <\/div>\r\n      <p class=\"v2201-note\">Shared world board - one row per pilot name with its best score, visible to everyone who plays.<\/p>\r\n    <\/section>\r\n\r\n    <!-- Game over screen -->\r\n    <section class=\"v2201-screen v2201-screen-over\">\r\n      <p class=\"v2201-gameover\">Jet down<\/p>\r\n      <div class=\"v2201-final\">\r\n        <span class=\"v2201-final-label\">Final score<\/span>\r\n        <span class=\"v2201-final-score\">0<\/span>\r\n        <span class=\"v2201-badge-slot\"><\/span>\r\n      <\/div>\r\n      <div class=\"v2201-board\">\r\n        <div class=\"v2201-board-title\">Top Pilots <span class=\"v2201-tag\">world board<\/span><\/div>\r\n        <div class=\"v2201-list v2201-list-over\"><\/div>\r\n      <\/div>\r\n      <div class=\"v2201-row\">\r\n        <button class=\"v2201-btn v2201-btn-again\" type=\"button\">Play again<\/button>\r\n        <button class=\"v2201-btn v2201-alt v2201-btn-rename\" type=\"button\">Change pilot<\/button>\r\n      <\/div>\r\n    <\/section>\r\n  <\/div>\r\n<\/div>\r\n\r\n\r\n<script>\r\n\/* =========================================================================\r\n   VIVID2201 - game logic (self-contained, no global variables)\r\n   Wrapped in an IIFE and scoped to the .v2201 container, so nothing here\r\n   collides with your theme or your other scripts. Load it in the footer,\r\n   or anywhere after the markup; it waits for the DOM either way.\r\n\r\n   Leaderboard note: the shared board uses window.storage, which only exists\r\n   inside Claude. On your own site it falls back to in-memory (resets on\r\n   reload). To make it a real shared board, wire a backend into lbLoad and\r\n   lbSave below. IP detection works on your own domain.\r\n   ========================================================================= *\/\r\n(function () {\r\n  function init() {\r\n    var root = document.querySelector('.v2201');\r\n    if (!root) return;                       \/\/ container not on this page\r\n    var $ = function (sel) { return root.querySelector(sel); };\r\n\r\n    \/* ---------------- Tunables ---------------- *\/\r\n    var CONFIG = {\r\n      gravity:      0.50,\r\n      flap:        -8.4,\r\n      maxFall:      13,\r\n      jetXFrac:     0.74,\r\n      shipBaseSpeed:3.4,\r\n      speedStep:    0.5,\r\n      speedEvery:   20,\r\n      maxMult:      4.0,\r\n      pointsPerShip:5,\r\n      gapMin:       330,\r\n      gapMax:       480,\r\n      vJitter:      200,\r\n      shipScaleMin: 0.72,\r\n      shipScaleMax: 1.42\r\n    };\r\n\r\n    var BASE_W = 900, BASE_H = 560;\r\n    var GROUND_Y = 528;\r\n    var JET_X = BASE_W * CONFIG.jetXFrac;\r\n\r\n    var JET  = { visW:72, visH:36, hitW:46, hitH:22 };\r\n    var SHIP = { visW:66, visH:52, hitW:46, hitH:34 };\r\n\r\n    \/* ---------------- Elements ---------------- *\/\r\n    var canvas = $('.v2201-canvas');\r\n    var ctx = canvas.getContext('2d');\r\n    var el = {\r\n      mute:        $('.v2201-mute'),\r\n      startScreen: $('.v2201-screen-start'),\r\n      overScreen:  $('.v2201-screen-over'),\r\n      name:        $('.v2201-name'),\r\n      startBtn:    $('.v2201-btn-start'),\r\n      againBtn:    $('.v2201-btn-again'),\r\n      renameBtn:   $('.v2201-btn-rename'),\r\n      startList:   $('.v2201-list-start'),\r\n      overList:    $('.v2201-list-over'),\r\n      finalScore:  $('.v2201-final-score'),\r\n      badge:       $('.v2201-badge-slot')\r\n    };\r\n\r\n    var DPR = Math.min(window.devicePixelRatio || 1, 2);\r\n    function sizeCanvas() {\r\n      DPR = Math.min(window.devicePixelRatio || 1, 2);\r\n      canvas.width = Math.round(BASE_W * DPR);\r\n      canvas.height = Math.round(BASE_H * DPR);\r\n      ctx.setTransform(DPR, 0, 0, DPR, 0, 0);\r\n    }\r\n    sizeCanvas();\r\n    window.addEventListener('resize', sizeCanvas);\r\n\r\n    var reduceMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;\r\n\r\n    \/* ---------------- State ---------------- *\/\r\n    var state = 'start';\r\n    var ready = false;\r\n    var jet = { x:JET_X, y:BASE_H*0.5, vy:0, thrust:0, tilt:0 };\r\n    var ships = [];\r\n    var particles = [];\r\n    var stars = [];\r\n    var score = 0, passed = 0, mult = 1.0;\r\n    var distSinceSpawn = 0, nextGap = CONFIG.gapMin, graceSteps = 48, lastShipY = null;\r\n    var speedFlash = 0, shake = 0, deathHandled = false;\r\n    var playerName = '';\r\n    var tNow = 0;\r\n\r\n    \/* ---------------- Stars ---------------- *\/\r\n    function makeStars() {\r\n      stars = [];\r\n      var layers = [\r\n        { n:46, spd:0.35, r:[0.6,1.1], a:[0.25,0.5] },\r\n        { n:30, spd:0.75, r:[0.9,1.6], a:[0.4,0.75] },\r\n        { n:16, spd:1.35, r:[1.4,2.3], a:[0.6,1.0] }\r\n      ];\r\n      layers.forEach(function (L) {\r\n        for (var i = 0; i < L.n; i++) {\r\n          stars.push({\r\n            x:Math.random()*BASE_W, y:Math.random()*BASE_H,\r\n            spd:L.spd, r:L.r[0]+Math.random()*(L.r[1]-L.r[0]),\r\n            a:L.a[0]+Math.random()*(L.a[1]-L.a[0]),\r\n            tw:Math.random()*6.28\r\n          });\r\n        }\r\n      });\r\n    }\r\n    makeStars();\r\n\r\n    \/* ---------------- Audio ---------------- *\/\r\n    var audioCtx = null, muted = false;\r\n    function ensureAudio() {\r\n      if (!audioCtx) { try { audioCtx = new (window.AudioContext || window.webkitAudioContext)(); } catch (e) {} }\r\n      if (audioCtx && audioCtx.state === 'suspended') audioCtx.resume();\r\n    }\r\n    function tone(freq, dur, type, vol, slideTo) {\r\n      if (muted || !audioCtx) return;\r\n      var t = audioCtx.currentTime;\r\n      var o = audioCtx.createOscillator(), g = audioCtx.createGain();\r\n      o.type = type || 'sine';\r\n      o.frequency.setValueAtTime(freq, t);\r\n      if (slideTo) o.frequency.exponentialRampToValueAtTime(Math.max(1, slideTo), t + dur);\r\n      g.gain.setValueAtTime(vol || 0.05, t);\r\n      g.gain.exponentialRampToValueAtTime(0.0001, t + dur);\r\n      o.connect(g); g.connect(audioCtx.destination);\r\n      o.start(t); o.stop(t + dur + 0.02);\r\n    }\r\n    var sfxFlap  = function () { tone(520, 0.09, 'triangle', 0.045, 250); };\r\n    var sfxScore = function () { tone(880, 0.10, 'sine',     0.06, 1300); };\r\n    var sfxSpeed = function () { tone(300, 0.20, 'sawtooth', 0.05, 780); };\r\n    function sfxCrash() {\r\n      if (muted || !audioCtx) return;\r\n      var t = audioCtx.currentTime;\r\n      tone(220, 0.55, 'sawtooth', 0.09, 55);\r\n      var n = audioCtx.sampleRate * 0.4;\r\n      var buf = audioCtx.createBuffer(1, n, audioCtx.sampleRate);\r\n      var d = buf.getChannelData(0);\r\n      for (var i = 0; i < n; i++) d[i] = (Math.random()*2-1) * Math.pow(1 - i\/n, 2);\r\n      var src = audioCtx.createBufferSource(); src.buffer = buf;\r\n      var g = audioCtx.createGain();\r\n      g.gain.setValueAtTime(0.13, t);\r\n      g.gain.exponentialRampToValueAtTime(0.0001, t + 0.4);\r\n      src.connect(g); g.connect(audioCtx.destination); src.start(t);\r\n    }\r\n\r\n    \/* ---------------- Player identity (IP + fallbacks) ---------------- *\/\r\n    var playerId = null;\r\n    function hash32(str) {\r\n      var h = 0x811c9dc5;\r\n      for (var i = 0; i < str.length; i++) { h ^= str.charCodeAt(i); h = Math.imul(h, 0x01000193); }\r\n      return (h >>> 0).toString(16).padStart(8, '0');\r\n    }\r\n    function fetchIP() {\r\n      var urls = ['https:\/\/api.ipify.org?format=json', 'https:\/\/api64.ipify.org?format=json'];\r\n      return urls.reduce(function (chain, u) {\r\n        return chain.then(function (found) {\r\n          if (found) return found;\r\n          return new Promise(function (resolve) {\r\n            var ctrl = new AbortController();\r\n            var to = setTimeout(function () { ctrl.abort(); }, 2000);\r\n            fetch(u, { signal: ctrl.signal, cache: 'no-store' })\r\n              .then(function (r) { clearTimeout(to); return r.ok ? r.json() : null; })\r\n              .then(function (j) { resolve(j && j.ip ? String(j.ip) : null); })\r\n              .catch(function () { clearTimeout(to); resolve(null); });\r\n          });\r\n        });\r\n      }, Promise.resolve(null));\r\n    }\r\n    function getDeviceId() {\r\n      return Promise.resolve().then(function () {\r\n        if (window.storage && window.storage.get) {\r\n          return window.storage.get('vivid2201_device_id', false).then(function (r) {\r\n            if (r && r.value) return r.value;\r\n            var id = 'd' + Math.random().toString(36).slice(2, 10) + Date.now().toString(36).slice(-4);\r\n            return Promise.resolve(window.storage.set('vivid2201_device_id', id, false)).then(function () { return id; }).catch(function () { return id; });\r\n          }).catch(function () { return 'd' + Math.random().toString(36).slice(2, 10); });\r\n        }\r\n        return 'd' + Math.random().toString(36).slice(2, 10);\r\n      });\r\n    }\r\n    function resolveIdentity() {\r\n      return fetchIP().then(function (ip) {\r\n        if (ip) { playerId = 'ip' + hash32('jd:' + ip); return playerId; }\r\n        return getDeviceId().then(function (dev) { playerId = 'dv' + hash32(dev); return playerId; });\r\n      });\r\n    }\r\n    var idReady = resolveIdentity();\r\n\r\n    \/* ---------------- Leaderboard ---------------- *\/\r\n    var LB_KEY = 'vivid2201_leaderboard_v1';\r\n    var memBoard = { data: [] };\r\n    function dedupeByName(list) {\r\n      var byName = {};\r\n      (list || []).forEach(function (e) {\r\n        var k = (e.name || '').toUpperCase().trim();\r\n        if (!k) return;\r\n        if (!byName[k] || e.score > byName[k].score) byName[k] = e;\r\n      });\r\n      var out = Object.keys(byName).map(function (k) { return byName[k]; });\r\n      out.sort(function (a, b) { return b.score - a.score || a.ts - b.ts; });\r\n      return out;\r\n    }\r\n    function lbLoad() {\r\n      if (window.storage && window.storage.get) {\r\n        return window.storage.get(LB_KEY, true).then(function (r) {\r\n          return (r && r.value) ? dedupeByName(JSON.parse(r.value)) : [];\r\n        }).catch(function () { return dedupeByName(memBoard.data.slice()); });\r\n      }\r\n      return Promise.resolve(dedupeByName(memBoard.data.slice()));\r\n    }\r\n    function lbSave(list) {\r\n      memBoard.data = list;\r\n      if (window.storage && window.storage.set) {\r\n        return Promise.resolve(window.storage.set(LB_KEY, JSON.stringify(list), true)).catch(function () {});\r\n      }\r\n      return Promise.resolve();\r\n    }\r\n    function lbSubmit(name, sc) {\r\n      return idReady.then(lbLoad).then(function (list) {\r\n        var id = playerId || 'anon';\r\n        var nm = ((name || 'PILOT').slice(0, 14).toUpperCase().trim()) || 'PILOT';\r\n        var entry = null;\r\n        for (var i = 0; i < list.length; i++) {\r\n          if ((list[i].name || '').toUpperCase().trim() === nm) { entry = list[i]; break; }\r\n        }\r\n        if (entry) {\r\n          if (sc > entry.score) { entry.score = sc; entry.ts = Date.now(); entry.id = id; }\r\n        } else {\r\n          entry = { name: nm, score: sc, ts: Date.now(), id: id };\r\n          list.push(entry);\r\n        }\r\n        var top = dedupeByName(list).slice(0, 100);\r\n        return lbSave(top).then(function () {\r\n          var saved = top.filter(function (e) { return (e.name || '').toUpperCase().trim() === nm; })[0] || entry;\r\n          return { top: top, entry: saved };\r\n        });\r\n      });\r\n    }\r\n\r\n    function escapeHtml(s) {\r\n      return String(s).replace(\/[&<>\"']\/g, function (c) {\r\n        return ({ '&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;' })[c];\r\n      });\r\n    }\r\n    function renderBoard(target, list, opts) {\r\n      opts = opts || {};\r\n      var limit = opts.limit || 8;\r\n      target.innerHTML = '';\r\n      if (!list || !list.length) {\r\n        target.innerHTML = '<div class=\"v2201-empty\">No scores yet. Be the first pilot on the board.<\/div>';\r\n        return;\r\n      }\r\n      var shown = list.slice(0, limit);\r\n      var medal = ['v2201-gold', 'v2201-silver', 'v2201-bronze'];\r\n      var meId = opts.meId || null;\r\n      var meTs = opts.meEntry ? opts.meEntry.ts : null;\r\n      shown.forEach(function (e, i) {\r\n        var mine = (meId && e.id === meId) || (meTs && e.ts === meTs);\r\n        var row = document.createElement('div');\r\n        row.className = 'v2201-lb-row' + (i < 3 ? ' ' + medal[i] : '') + (mine ? ' v2201-me' : '');\r\n        row.innerHTML = '<span class=\"v2201-rk\">' + (i + 1) + '<\/span><span class=\"v2201-nm\">' + escapeHtml(e.name) + '<\/span><span class=\"v2201-sc\">' + e.score + '<\/span>';\r\n        target.appendChild(row);\r\n      });\r\n      if (opts.meEntry) {\r\n        var idx = -1;\r\n        for (var j = 0; j < list.length; j++) {\r\n          if (list[j].ts === opts.meEntry.ts && list[j].name === opts.meEntry.name) { idx = j; break; }\r\n        }\r\n        if (idx >= limit) {\r\n          var sep = document.createElement('div'); sep.className = 'v2201-sep'; target.appendChild(sep);\r\n          var e2 = list[idx];\r\n          var row2 = document.createElement('div');\r\n          row2.className = 'v2201-lb-row v2201-me';\r\n          row2.innerHTML = '<span class=\"v2201-rk\">' + (idx + 1) + '<\/span><span class=\"v2201-nm\">' + escapeHtml(e2.name) + '<\/span><span class=\"v2201-sc\">' + e2.score + '<\/span>';\r\n          target.appendChild(row2);\r\n        }\r\n      }\r\n    }\r\n\r\n    \/* ---------------- Helpers ---------------- *\/\r\n    function clamp(v, a, b) { return v < a ? a : (v > b ? b : v); }\r\n    function rand(a, b) { return a + Math.random() * (b - a); }\r\n    function shipSpeed() { return CONFIG.shipBaseSpeed * mult; }\r\n\r\n    function spawnShip() {\r\n      var sc = rand(CONFIG.shipScaleMin, CONFIG.shipScaleMax);\r\n      var half = (SHIP.visH * sc) \/ 2;\r\n      var top = 58 + half, bot = GROUND_Y - 40 - half;\r\n      var y;\r\n      if (lastShipY != null) y = clamp(lastShipY + rand(-CONFIG.vJitter, CONFIG.vJitter), top, bot);\r\n      else y = rand(top, bot);\r\n      lastShipY = y;\r\n      ships.push({ x:-50*sc, y:y, sc:sc, counted:false, glow:Math.random()*6.28, bob:Math.random()*6.28 });\r\n    }\r\n\r\n    function flap() {\r\n      if (state !== 'playing') return;\r\n      if (!ready) {\r\n        ready = true;\r\n        graceSteps = 36;\r\n        distSinceSpawn = 0;\r\n        nextGap = rand(CONFIG.gapMin, CONFIG.gapMax);\r\n      }\r\n      jet.vy = CONFIG.flap;\r\n      jet.thrust = 1;\r\n      sfxFlap();\r\n      for (var i = 0; i < 4; i++) {\r\n        particles.push({\r\n          x:jet.x+22, y:jet.y+rand(-4,4),\r\n          vx:rand(2.5,5.5), vy:rand(-1.2,1.2),\r\n          life:rand(14,24), max:24, r:rand(1.5,3.2),\r\n          col:i%2 ? '#ffd27a' : '#ff8a3c', g:0.02\r\n        });\r\n      }\r\n    }\r\n\r\n    function boom(x, y) {\r\n      var N = reduceMotion ? 14 : 34;\r\n      for (var i = 0; i < N; i++) {\r\n        var a = Math.random()*6.28, s = rand(1.5, 7.5);\r\n        particles.push({\r\n          x:x, y:y, vx:Math.cos(a)*s, vy:Math.sin(a)*s,\r\n          life:rand(26,52), max:52, r:rand(1.8,4.5),\r\n          col:['#fff2c2','#ffb020','#ff5a2c','#35e0ff'][i%4], g:0.12\r\n        });\r\n      }\r\n    }\r\n\r\n    function die() {\r\n      if (state !== 'playing' || deathHandled) return;\r\n      deathHandled = true;\r\n      state = 'dead';\r\n      shake = reduceMotion ? 6 : 26;\r\n      boom(jet.x, jet.y);\r\n      sfxCrash();\r\n      setTimeout(showGameOver, 850);\r\n    }\r\n\r\n    \/* ---------------- Reset \/ start ---------------- *\/\r\n    function resetGame() {\r\n      jet = { x:JET_X, y:BASE_H*0.5, vy:0, thrust:0, tilt:0 };\r\n      ships = []; particles = [];\r\n      score = 0; passed = 0; mult = 1.0;\r\n      distSinceSpawn = 0; nextGap = rand(CONFIG.gapMin, CONFIG.gapMax);\r\n      graceSteps = 0; lastShipY = null;\r\n      speedFlash = 0; shake = 0; deathHandled = false;\r\n      ready = false;\r\n    }\r\n    function startRun() {\r\n      var raw = (el.name.value || '').trim();\r\n      playerName = raw ? raw.toUpperCase() : 'PILOT';\r\n      ensureAudio();\r\n      resetGame();\r\n      hide(el.startScreen); hide(el.overScreen);\r\n      state = 'playing';\r\n    }\r\n\r\n    \/* ---------------- Update ---------------- *\/\r\n    function update() {\r\n      tNow++;\r\n\r\n      for (var si = 0; si < stars.length; si++) {\r\n        var s = stars[si];\r\n        s.x += s.spd; s.tw += 0.05;\r\n        if (s.x > BASE_W + 2) { s.x = -2; s.y = Math.random()*BASE_H; }\r\n      }\r\n\r\n      if (state === 'playing' && !ready) {\r\n        jet.y = BASE_H*0.5 + Math.sin(tNow*0.05)*10;\r\n        jet.vy = 0;\r\n        jet.thrust *= 0.85;\r\n        jet.tilt += (0 - jet.tilt) * 0.2;\r\n\r\n      } else if (state === 'playing') {\r\n        jet.vy += CONFIG.gravity;\r\n        if (jet.vy > CONFIG.maxFall) jet.vy = CONFIG.maxFall;\r\n        jet.y += jet.vy;\r\n        jet.thrust *= 0.85;\r\n        jet.tilt += ((clamp(jet.vy, -9, 12) * 0.045) - jet.tilt) * 0.2;\r\n\r\n        if (jet.y - JET.hitH\/2 < 2) { jet.y = 2 + JET.hitH\/2; if (jet.vy < 0) jet.vy = 0; }\r\n        if (jet.y + JET.hitH\/2 >= GROUND_Y) { jet.y = GROUND_Y - JET.hitH\/2; die(); }\r\n\r\n        if (graceSteps > 0) graceSteps--;\r\n        else {\r\n          distSinceSpawn += shipSpeed();\r\n          if (distSinceSpawn >= nextGap) { spawnShip(); distSinceSpawn = 0; nextGap = rand(CONFIG.gapMin, CONFIG.gapMax); }\r\n        }\r\n\r\n        var sp = shipSpeed();\r\n        for (var i = 0; i < ships.length; i++) {\r\n          var sh = ships[i];\r\n          sh.x += sp; sh.glow += 0.08; sh.bob += 0.05;\r\n          if (!sh.counted && sh.x > jet.x) {\r\n            sh.counted = true;\r\n            score += CONFIG.pointsPerShip;\r\n            passed++;\r\n            sfxScore();\r\n            if (passed % CONFIG.speedEvery === 0 && mult < CONFIG.maxMult) {\r\n              mult = Math.min(CONFIG.maxMult, mult + CONFIG.speedStep);\r\n              speedFlash = 66;\r\n              sfxSpeed();\r\n            }\r\n          }\r\n          if (Math.abs(jet.x - sh.x) < (JET.hitW + SHIP.hitW*sh.sc)\/2 &&\r\n              Math.abs(jet.y - sh.y) < (JET.hitH + SHIP.hitH*sh.sc)\/2) {\r\n            die();\r\n          }\r\n        }\r\n        ships = ships.filter(function (s2) { return s2.x < BASE_W + 90; });\r\n        if (speedFlash > 0) speedFlash--;\r\n\r\n      } else if (state === 'dead') {\r\n        var sp2 = shipSpeed() * 0.4;\r\n        for (var k = 0; k < ships.length; k++) { ships[k].x += sp2; ships[k].glow += 0.08; }\r\n        ships = ships.filter(function (s3) { return s3.x < BASE_W + 90; });\r\n      }\r\n\r\n      for (var p = 0; p < particles.length; p++) {\r\n        var pt = particles[p];\r\n        pt.x += pt.vx; pt.y += pt.vy; pt.vy += pt.g; pt.vx *= 0.99; pt.life--;\r\n      }\r\n      particles = particles.filter(function (q) { return q.life > 0; });\r\n\r\n      if (shake > 0) shake *= 0.86;\r\n    }\r\n\r\n    \/* ---------------- Drawing ---------------- *\/\r\n    function radial(x, y, r, col) {\r\n      var g = ctx.createRadialGradient(x, y, 0, x, y, r);\r\n      g.addColorStop(0, col); g.addColorStop(1, 'rgba(0,0,0,0)');\r\n      ctx.fillStyle = g; ctx.fillRect(x - r, y - r, r * 2, r * 2);\r\n    }\r\n    function drawBackground() {\r\n      var g = ctx.createLinearGradient(0, 0, 0, BASE_H);\r\n      g.addColorStop(0, '#05060f'); g.addColorStop(0.55, '#080c20'); g.addColorStop(1, '#0a1230');\r\n      ctx.fillStyle = g; ctx.fillRect(0, 0, BASE_W, BASE_H);\r\n      ctx.save();\r\n      ctx.globalCompositeOperation = 'lighter';\r\n      radial(BASE_W*0.22, BASE_H*0.28, 260, 'rgba(90,40,200,0.16)');\r\n      radial(BASE_W*0.78, BASE_H*0.7, 300, 'rgba(20,120,190,0.14)');\r\n      ctx.restore();\r\n    }\r\n    function drawStars() {\r\n      for (var i = 0; i < stars.length; i++) {\r\n        var s = stars[i];\r\n        var tw = 0.6 + 0.4*Math.sin(s.tw);\r\n        ctx.globalAlpha = s.a * tw;\r\n        ctx.fillStyle = s.r > 1.6 ? '#bfe4ff' : '#ffffff';\r\n        ctx.beginPath(); ctx.arc(s.x, s.y, s.r, 0, 6.2832); ctx.fill();\r\n      }\r\n      ctx.globalAlpha = 1;\r\n    }\r\n    function drawGround() {\r\n      var gy = GROUND_Y;\r\n      var g = ctx.createLinearGradient(0, gy, 0, BASE_H);\r\n      g.addColorStop(0, 'rgba(53,224,255,0.16)'); g.addColorStop(1, 'rgba(4,7,18,0.9)');\r\n      ctx.fillStyle = g; ctx.fillRect(0, gy, BASE_W, BASE_H - gy);\r\n      ctx.strokeStyle = 'rgba(53,224,255,0.85)'; ctx.lineWidth = 2;\r\n      ctx.shadowColor = 'rgba(53,224,255,0.8)'; ctx.shadowBlur = 12;\r\n      ctx.beginPath(); ctx.moveTo(0, gy + 0.5); ctx.lineTo(BASE_W, gy + 0.5); ctx.stroke();\r\n      ctx.shadowBlur = 0;\r\n      ctx.fillStyle = 'rgba(255,93,108,0.5)';\r\n      var off = (tNow * 2) % 40;\r\n      for (var x = -40; x < BASE_W + 40; x += 40) {\r\n        ctx.beginPath();\r\n        ctx.moveTo(x + off, gy + 6); ctx.lineTo(x + off + 14, gy + 6); ctx.lineTo(x + off + 7, gy + 14);\r\n        ctx.closePath(); ctx.fill();\r\n      }\r\n    }\r\n    function drawJet(x, y, tilt, thrust, bobbing) {\r\n      ctx.save();\r\n      ctx.translate(x, y);\r\n      ctx.rotate(tilt || 0);\r\n      var flame = 10 + thrust*22 + Math.sin(tNow*0.8)*2;\r\n      var fg = ctx.createLinearGradient(24, 0, 24 + flame, 0);\r\n      fg.addColorStop(0, 'rgba(255,240,180,0.95)');\r\n      fg.addColorStop(0.5, 'rgba(255,150,40,0.85)');\r\n      fg.addColorStop(1, 'rgba(255,60,30,0)');\r\n      ctx.fillStyle = fg;\r\n      ctx.beginPath(); ctx.moveTo(22, -7); ctx.lineTo(24 + flame, 0); ctx.lineTo(22, 7); ctx.closePath(); ctx.fill();\r\n      ctx.fillStyle = '#6f86a6';\r\n      ctx.beginPath(); ctx.moveTo(6, -4); ctx.lineTo(20, -20); ctx.lineTo(22, -4); ctx.closePath(); ctx.fill();\r\n      ctx.beginPath(); ctx.moveTo(6, 4); ctx.lineTo(20, 20); ctx.lineTo(22, 4); ctx.closePath(); ctx.fill();\r\n      var bg = ctx.createLinearGradient(0, -12, 0, 12);\r\n      bg.addColorStop(0, '#f4f9ff'); bg.addColorStop(0.5, '#cdd9ea'); bg.addColorStop(1, '#8fa2bd');\r\n      ctx.fillStyle = bg;\r\n      ctx.beginPath();\r\n      ctx.moveTo(-36, 0);\r\n      ctx.quadraticCurveTo(-14, -12, 8, -10);\r\n      ctx.lineTo(24, -6); ctx.lineTo(24, 6); ctx.lineTo(8, 10);\r\n      ctx.quadraticCurveTo(-14, 12, -36, 0);\r\n      ctx.closePath(); ctx.fill();\r\n      ctx.strokeStyle = 'rgba(53,224,255,0.9)'; ctx.lineWidth = 1.4;\r\n      ctx.beginPath(); ctx.moveTo(-36, 0); ctx.lineTo(-16, 0); ctx.stroke();\r\n      ctx.fillStyle = '#0b1c2a';\r\n      ctx.beginPath(); ctx.ellipse(-8, -2, 8, 4.5, 0, 0, 6.28); ctx.fill();\r\n      ctx.fillStyle = 'rgba(53,224,255,0.9)';\r\n      ctx.beginPath(); ctx.ellipse(-9, -3, 4, 2.2, 0, 0, 6.28); ctx.fill();\r\n      ctx.restore();\r\n    }\r\n    function drawShip(s) {\r\n      var bob = Math.sin(s.bob) * 3;\r\n      var glow = 0.55 + 0.45*Math.sin(s.glow);\r\n      ctx.save();\r\n      ctx.translate(s.x, s.y + bob);\r\n      ctx.scale(s.sc, s.sc);\r\n      ctx.save();\r\n      ctx.globalCompositeOperation = 'lighter';\r\n      var ag = ctx.createRadialGradient(0, 0, 4, 0, 0, 40);\r\n      ag.addColorStop(0, 'rgba(178,59,255,' + (0.35*glow) + ')');\r\n      ag.addColorStop(1, 'rgba(178,59,255,0)');\r\n      ctx.fillStyle = ag; ctx.beginPath(); ctx.arc(0, 0, 40, 0, 6.28); ctx.fill();\r\n      ctx.restore();\r\n      var bg = ctx.createLinearGradient(0, -6, 0, 10);\r\n      bg.addColorStop(0, '#4a4f66'); bg.addColorStop(1, '#20233a');\r\n      ctx.fillStyle = bg;\r\n      ctx.beginPath(); ctx.ellipse(0, 6, 30, 11, 0, 0, 6.28); ctx.fill();\r\n      ctx.strokeStyle = 'rgba(200,210,255,0.35)'; ctx.lineWidth = 1.2;\r\n      ctx.beginPath(); ctx.ellipse(0, 6, 30, 11, 0, 0, 6.28); ctx.stroke();\r\n      var dg = ctx.createLinearGradient(0, -16, 0, 4);\r\n      dg.addColorStop(0, 'rgba(210,150,255,0.95)');\r\n      dg.addColorStop(1, 'rgba(120,40,190,0.85)');\r\n      ctx.fillStyle = dg;\r\n      ctx.beginPath(); ctx.ellipse(0, 2, 15, 14, 0, Math.PI, 0); ctx.fill();\r\n      ctx.fillStyle = 'rgba(255,255,255,0.5)';\r\n      ctx.beginPath(); ctx.ellipse(-4, -4, 3.5, 4.5, -0.4, 0, 6.28); ctx.fill();\r\n      ctx.fillStyle = 'rgba(57,255,136,' + (0.6 + 0.4*glow) + ')';\r\n      ctx.beginPath(); ctx.arc(0, -1, 3.4, 0, 6.28); ctx.fill();\r\n      ctx.fillStyle = 'rgba(255,93,162,' + (0.6 + 0.4*glow) + ')';\r\n      for (var i = -2; i <= 2; i++) { ctx.beginPath(); ctx.arc(i*11, 9, 2, 0, 6.28); ctx.fill(); }\r\n      ctx.restore();\r\n    }\r\n    function drawParticles() {\r\n      ctx.save();\r\n      ctx.globalCompositeOperation = 'lighter';\r\n      for (var i = 0; i < particles.length; i++) {\r\n        var p = particles[i];\r\n        ctx.globalAlpha = Math.max(0, p.life \/ p.max);\r\n        ctx.fillStyle = p.col;\r\n        ctx.beginPath(); ctx.arc(p.x, p.y, p.r, 0, 6.28); ctx.fill();\r\n      }\r\n      ctx.restore();\r\n      ctx.globalAlpha = 1;\r\n    }\r\n    function drawHUD() {\r\n      var g = ctx.createLinearGradient(0, 0, 0, 66);\r\n      g.addColorStop(0, 'rgba(4,7,18,0.75)'); g.addColorStop(1, 'rgba(4,7,18,0)');\r\n      ctx.fillStyle = g; ctx.fillRect(0, 0, BASE_W, 66);\r\n      ctx.textBaseline = 'top';\r\n      ctx.fillStyle = 'rgba(147,164,198,0.9)';\r\n      ctx.font = '700 12px -apple-system,Segoe UI,Roboto,Arial';\r\n      ctx.fillText('SCORE', 22, 12);\r\n      ctx.fillStyle = '#eaf2ff';\r\n      ctx.font = '900 30px -apple-system,Segoe UI,Roboto,Arial';\r\n      ctx.fillText(String(score).padStart(3, '0'), 22, 26);\r\n      ctx.textAlign = 'right';\r\n      ctx.fillStyle = 'rgba(147,164,198,0.9)';\r\n      ctx.font = '700 12px -apple-system,Segoe UI,Roboto,Arial';\r\n      ctx.fillText('SHIPS', BASE_W - 22, 12);\r\n      ctx.fillStyle = '#35e0ff';\r\n      ctx.font = '900 22px -apple-system,Segoe UI,Roboto,Arial';\r\n      ctx.fillText(String(passed), BASE_W - 22, 26);\r\n      ctx.fillStyle = 'rgba(147,164,198,0.9)';\r\n      ctx.font = '700 12px -apple-system,Segoe UI,Roboto,Arial';\r\n      ctx.fillText('SPEED', BASE_W - 110, 12);\r\n      ctx.fillStyle = mult >= CONFIG.maxMult ? '#ff5d6c' : '#b23bff';\r\n      ctx.font = '900 22px -apple-system,Segoe UI,Roboto,Arial';\r\n      ctx.fillText('x' + mult.toFixed(1), BASE_W - 110, 26);\r\n      ctx.textAlign = 'left';\r\n      if (speedFlash > 0) {\r\n        var a = Math.min(1, speedFlash\/20) * (0.6 + 0.4*Math.sin(tNow*0.6));\r\n        ctx.globalAlpha = a;\r\n        ctx.textAlign = 'center';\r\n        ctx.fillStyle = '#ffb020';\r\n        ctx.font = '900 34px -apple-system,Segoe UI,Roboto,Arial';\r\n        ctx.fillText('SWARM ACCELERATING', BASE_W\/2, BASE_H*0.32);\r\n        ctx.textAlign = 'left';\r\n        ctx.globalAlpha = 1;\r\n      }\r\n    }\r\n    function drawVignette() {\r\n      var g = ctx.createRadialGradient(BASE_W\/2, BASE_H\/2, BASE_H*0.35, BASE_W\/2, BASE_H\/2, BASE_H*0.8);\r\n      g.addColorStop(0, 'rgba(0,0,0,0)'); g.addColorStop(1, 'rgba(0,0,0,0.45)');\r\n      ctx.fillStyle = g; ctx.fillRect(0, 0, BASE_W, BASE_H);\r\n    }\r\n    function render() {\r\n      ctx.clearRect(0, 0, BASE_W, BASE_H);\r\n      ctx.save();\r\n      if (shake > 0.5) ctx.translate((Math.random()-0.5)*shake, (Math.random()-0.5)*shake);\r\n      drawBackground();\r\n      drawStars();\r\n      drawGround();\r\n      for (var i = 0; i < ships.length; i++) drawShip(ships[i]);\r\n      if (state === 'start') {\r\n        drawJet(jet.x, BASE_H*0.5 + Math.sin(tNow*0.05)*10, -0.05, 0.4, true);\r\n      } else if (state !== 'dead' || particles.length) {\r\n        if (state !== 'dead') drawJet(jet.x, jet.y, jet.tilt, jet.thrust, false);\r\n      }\r\n      drawParticles();\r\n      ctx.restore();\r\n      drawVignette();\r\n      if (state === 'playing' || state === 'dead') drawHUD();\r\n      if (state === 'playing' && !ready) {\r\n        var a = 0.55 + 0.45*Math.sin(tNow*0.12);\r\n        ctx.save();\r\n        ctx.globalAlpha = a;\r\n        ctx.textAlign = 'center';\r\n        ctx.fillStyle = '#eaf2ff';\r\n        ctx.font = '900 26px -apple-system,Segoe UI,Roboto,Arial';\r\n        ctx.fillText('TAP  \/  CLICK  \/  SPACE  TO  FLY', BASE_W\/2, BASE_H*0.66);\r\n        ctx.fillStyle = 'rgba(147,164,198,0.95)';\r\n        ctx.font = '700 13px -apple-system,Segoe UI,Roboto,Arial';\r\n        ctx.fillText('the jet holds steady until you launch', BASE_W\/2, BASE_H*0.66 + 30);\r\n        ctx.restore();\r\n        ctx.textAlign = 'left';\r\n      }\r\n    }\r\n\r\n    \/* ---------------- Loop ---------------- *\/\r\n    var STEP = 1000\/60;\r\n    var acc = 0, last = performance.now(), paused = false;\r\n    function frame(now) {\r\n      requestAnimationFrame(frame);\r\n      if (paused) { last = now; return; }\r\n      acc += now - last; last = now;\r\n      if (acc > 250) acc = 250;\r\n      while (acc >= STEP) { update(); acc -= STEP; }\r\n      render();\r\n    }\r\n    requestAnimationFrame(frame);\r\n    document.addEventListener('visibilitychange', function () { paused = document.hidden; });\r\n\r\n    \/* ---------------- Input ---------------- *\/\r\n    canvas.addEventListener('pointerdown', function (e) {\r\n      if (state === 'playing') { e.preventDefault(); flap(); }\r\n    });\r\n    document.addEventListener('keydown', function (e) {\r\n      var tag = (e.target && e.target.tagName) || '';\r\n      if (\/^(input|textarea|select)$\/i.test(tag)) return;   \/\/ do not hijack typing elsewhere\r\n      if (e.code === 'Space' || e.code === 'ArrowUp' || e.key === 'w' || e.key === 'W') {\r\n        if (state === 'playing') { e.preventDefault(); flap(); }\r\n      }\r\n    });\r\n\r\n    \/* ---------------- Screens ---------------- *\/\r\n    function show(node) { node.classList.add('v2201-shown'); }\r\n    function hide(node) { node.classList.remove('v2201-shown'); }\r\n\r\n    function refreshStartBoard() {\r\n      lbLoad().then(function (list) { renderBoard(el.startList, list, { limit: 5, meId: playerId }); });\r\n    }\r\n    function showGameOver() {\r\n      el.finalScore.textContent = score;\r\n      lbSubmit(playerName, score).then(function (res) {\r\n        var top = res.top, entry = res.entry;\r\n        var rank = -1;\r\n        for (var i = 0; i < top.length; i++) {\r\n          if (top[i].ts === entry.ts && top[i].name === entry.name) { rank = i; break; }\r\n        }\r\n        var badge = '';\r\n        if (score > 0) {\r\n          if (rank === 0) badge = '<span class=\"v2201-badge\">New #1 pilot<\/span>';\r\n          else if (rank > 0 && rank < 3) badge = '<span class=\"v2201-badge\">Top 3 finish<\/span>';\r\n        }\r\n        el.badge.innerHTML = badge;\r\n        renderBoard(el.overList, top, { limit: 8, meId: playerId, meEntry: entry });\r\n        state = 'dead';\r\n        show(el.overScreen);\r\n      });\r\n    }\r\n\r\n    \/* ---------------- Buttons ---------------- *\/\r\n    el.startBtn.addEventListener('click', startRun);\r\n    el.name.addEventListener('keydown', function (e) { if (e.key === 'Enter') startRun(); });\r\n    el.againBtn.addEventListener('click', function () { hide(el.overScreen); startRun(); });\r\n    el.renameBtn.addEventListener('click', function () {\r\n      hide(el.overScreen); state = 'start';\r\n      refreshStartBoard();\r\n      show(el.startScreen);\r\n      el.name.value = (playerName === 'PILOT') ? '' : playerName;\r\n      el.name.focus();\r\n    });\r\n    el.mute.addEventListener('click', function () {\r\n      muted = !muted;\r\n      el.mute.innerHTML = muted ? '\\uD83D\\uDD07' : '\\uD83D\\uDD0A';\r\n    });\r\n\r\n    \/* ---------------- Boot ---------------- *\/\r\n    refreshStartBoard();\r\n    idReady.then(refreshStartBoard).catch(function () {});\r\n  }\r\n\r\n  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);\r\n  else init();\r\n})();\r\n\r\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-7ff7fa2f e-flex e-con-boxed e-con e-parent\" data-id=\"7ff7fa2f\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-67ed46a5 elementor-widget elementor-widget-text-editor\" data-id=\"67ed46a5\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t\n<p class=\"wp-block-paragraph\">A one-touch arcade dodger built around a single move. You fly a lone fighter into a tide of <a href=\"https:\/\/theandroidapk.com\/blog\/javitful-android-apps-for-gamers-the-best-picks-for-endless-fun\/\">alien ships<\/a> pouring in from across the screen and the whole thing comes down to timing your lift before the jet drops into something solid.<\/p>\n\n<h1 class=\"wp-block-heading\"><strong>At a glance<\/strong><\/h1>\n\n<figure class=\"wp-block-table\">\n<table class=\"has-fixed-layout\">\n<tbody>\n<tr>\n<td><strong>Type<\/strong><\/td>\n<td>One-touch arcade dodger<\/td>\n<\/tr>\n<tr>\n<td><strong>Controls<\/strong><\/td>\n<td>Tap, click, or press Space<\/td>\n<\/tr>\n<tr>\n<td><strong>Points per ship<\/strong><\/td>\n<td>5<\/td>\n<\/tr>\n<tr>\n<td><strong>Difficulty<\/strong><\/td>\n<td>Speeds up every 20 ships<\/td>\n<\/tr>\n<tr>\n<td><strong>Goal<\/strong><\/td>\n<td>Top the shared world board<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<!-- =====================================================================\n     VIVID2201 - PLAY BUTTON for your article.\n     Paste this into your post wherever you want the button to appear.\n     It is a plain link with only CSS - NO JavaScript - so it works in any\n     WordPress block, Text widget, or Elementor widget, and no cache or\n     optimize plugin can break it.\n\n     >>> ONE THING TO DO: change the href URL below to the address where you\n         uploaded vivid2201-game.html on your site. For example:\n      https:\/\/theandroidapk.com\/blog\/vivid2201\/\n     ===================================================================== -->\n<h1 class=\"wp-block-heading\"><strong>The mission<\/strong><\/h1>\n\n<p class=\"wp-block-paragraph\">The swarm slipped past the outer stations sometime in the night and by the time anyone made sense of what they were seeing it was already a river of hulls bearing down on the inner line. Command emptied the hangars, every fighter it had going up at once, only that is not your part of the story, because when the scramble order reached your deck there was one bird left fueled and it had your name painted on the side.<\/p>\n\n<p class=\"wp-block-paragraph\">So you are out there alone, no wingman off your shoulder and no second run if you clip a wing. The alien ships never fire, they have never needed to, they just keep coming in rows and you thread between them or you don&#8217;t. Every hull you slide past is one the line behind you never has to face and that is the whole of the job really, keep her flying and let the next gap decide how far you get.<\/p>\n\n<h1 class=\"wp-block-heading\"><strong>Flying the jet<\/strong><\/h1>\n\n<p class=\"wp-block-paragraph\">The jet falls on its own, so the entire game is you fighting that fall with small taps. Tap and it lifts, ease off and it drifts back down and once you stop stabbing at the button and settle into a light steady rhythm it flies a lot smoother than it does when you panic. Overcook the taps and you float up into a hull, go too light and you sink into one, the line you want sits somewhere in the middle and you find it by feel.<\/p>\n\n<ul class=\"wp-block-list\">\n<li>Tap the screen, click, or press Space to lift the jet.<\/li>\n\n<li>Nothing to hold down, it is single taps that keep you airborne.<\/li>\n\n<li>The ceiling only bumps you back, but touching the ground ends the run.<\/li>\n<\/ul>\n\n<h1 class=\"wp-block-heading\"><strong>How a run plays out<\/strong><\/h1>\n\n<ol class=\"wp-block-list\">\n<li>The jet hangs in place at the start and waits on you, it will not drop until your first input, so take a second to read the lane.<\/li>\n\n<li>Your first tap launches the run and from that point gravity is always working against you.<\/li>\n\n<li>Alien ships slide in from the left at mixed heights and sizes and you weave through the gaps between them.<\/li>\n\n<li>Every ship you clear adds five points to a counter that ticks up live while you fly.<\/li>\n\n<li>Clip a hull or scrape the ground and the run is over, your score drops straight onto the board.<\/li>\n<\/ol>\n\n<h1 class=\"wp-block-heading\"><strong>Scoring and the world board<\/strong><\/h1>\n\n<figure class=\"wp-block-image size-full\"><img fetchpriority=\"high\" decoding=\"async\" width=\"537\" height=\"299\" class=\"wp-image-16768\" src=\"https:\/\/theandroidapk.com\/blog\/wp-content\/uploads\/2026\/07\/Scoring-and-the-world-board.webp\" alt=\"Scoring and the world board\" srcset=\"https:\/\/theandroidapk.com\/blog\/wp-content\/uploads\/2026\/07\/Scoring-and-the-world-board.webp 537w, https:\/\/theandroidapk.com\/blog\/wp-content\/uploads\/2026\/07\/Scoring-and-the-world-board-300x167.webp 300w, https:\/\/theandroidapk.com\/blog\/wp-content\/uploads\/2026\/07\/Scoring-and-the-world-board-60x33.webp 60w\" sizes=\"(max-width: 537px) 100vw, 537px\" \/><\/figure>\n\n<p class=\"wp-block-paragraph\">Points stay simple and they update live in the corner while you fly. The real pressure is the speed, it holds steady at the start but keeps stepping up the further you get, until the lanes are arriving faster than you can comfortably read them. That climb is the actual opponent, not any one ship sitting in front of you.<\/p>\n\n<ul class=\"wp-block-list\">\n<li>Each ship you get past is worth five points.<\/li>\n\n<li>The swarm shifts up half a gear for every twenty ships you clear and it keeps ratcheting from there.<\/li>\n\n<li>One pilot name holds one row on the board and it only ever keeps your highest score.<\/li>\n<\/ul>\n\n<p class=\"wp-block-paragraph\">The board itself is shared, so everyone who plays is looking at the same names, which means beating it is not only about topping your own record, it is about sitting above the last pilot who figured their spot was safe.<\/p>\n\n<h1 class=\"wp-block-heading\"><strong>Pilot tips<\/strong><\/h1>\n\n<ul class=\"wp-block-list\">\n<li><strong>Read the lane before it reaches you. <\/strong>Look at where the next ship sits while you are still clearing the current one, not when it is already on top of you, that half second of warning is most of the game.<\/li>\n\n<li><strong>Big hulls need an earlier decision. <\/strong>Commit your line while there is still room, because a large ship leaves you far less gap to correct if you leave the move late.<\/li>\n\n<li><strong>Feed small inputs instead of fighting it. <\/strong>The jet settles into a smooth glide on light steady taps, but stab at the button and it starts bouncing between the taps and the fall.<\/li>\n\n<li><strong>Every speed step steals reaction time. <\/strong>The run does not get harder because the ships change, it gets harder because you get less warning, so the further you go the earlier you have to read each gap.<\/li>\n<\/ul>\n\n<p class=\"wp-block-paragraph\">Vivid2201 player guide. Tap, click or press Space to fly. Dodge the swarm, climb the board.<\/p>\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>&#128266; Vivid2201 Alien Gauntlet &#8211; thread your fighter through the swarm and rack up the score. Tap, click, or press Space to lift the jet. Gravity pulls it down, so keep tapping. Dodge every alien ship, +5 points each. Do not hit the ground. Every 20 ships you pass, the swarm speeds up. Start run Top Pilots world board Loading scores&#8230; Shared world board &#8211; one row per pilot name with its best score, visible to everyone who plays. Jet down Final score 0 Top Pilots world board Play again Change pilot A one-touch arcade dodger built around a single<\/p>\n","protected":false},"author":8,"featured_media":16767,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9],"tags":[2044,2039,2043,2040,2041,2045,2042,2046],"class_list":["post-16766","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-games","tag-alien-dodger","tag-endless-arcade-game","tag-high-score-game","tag-jet-game","tag-online-leaderboard-game","tag-reflex-game","tag-survival-arcade-game","tag-tap-to-play-game"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.8 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Vivid2201: Dodge the Swarm, Top the Board - AndroidAPK<\/title>\n<meta name=\"description\" content=\"Play Vivid2201, the one-touch arcade dodger. Fly a lone jet through the alien swarm, score five points a ship and race to the top of the world board.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/theandroidapk.com\/blog\/vivid2201-dodge-the-swarm-top-the-board\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Vivid2201: Dodge the Swarm, Top the Board - AndroidAPK\" \/>\n<meta property=\"og:description\" content=\"Play Vivid2201, the one-touch arcade dodger. Fly a lone jet through the alien swarm, score five points a ship and race to the top of the world board.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/theandroidapk.com\/blog\/vivid2201-dodge-the-swarm-top-the-board\/\" \/>\n<meta property=\"og:site_name\" content=\"AndroidAPK\" \/>\n<meta property=\"article:published_time\" content=\"2026-07-01T08:35:45+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-07-01T10:04:05+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/theandroidapk.com\/blog\/wp-content\/uploads\/2026\/07\/Vivid2201-Dodge-the-Swarm-Top-the-Board.webp\" \/>\n\t<meta property=\"og:image:width\" content=\"703\" \/>\n\t<meta property=\"og:image:height\" content=\"425\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/webp\" \/>\n<meta name=\"author\" content=\"Chandio\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Chandio\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"13 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/vivid2201-dodge-the-swarm-top-the-board\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/vivid2201-dodge-the-swarm-top-the-board\\\/\"},\"author\":{\"name\":\"Chandio\",\"@id\":\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/#\\\/schema\\\/person\\\/023ca27ae7348fbc0506b1435ff35e78\"},\"headline\":\"Vivid2201: Dodge the Swarm, Top the Board\",\"datePublished\":\"2026-07-01T08:35:45+00:00\",\"dateModified\":\"2026-07-01T10:04:05+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/vivid2201-dodge-the-swarm-top-the-board\\\/\"},\"wordCount\":871,\"commentCount\":0,\"image\":{\"@id\":\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/vivid2201-dodge-the-swarm-top-the-board\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/Vivid2201-Dodge-the-Swarm-Top-the-Board.webp\",\"keywords\":[\"alien dodger\",\"endless arcade game\",\"high score game\",\"jet game\",\"online leaderboard game\",\"reflex game\",\"survival arcade game\",\"tap to play game\"],\"articleSection\":[\"Games\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/vivid2201-dodge-the-swarm-top-the-board\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/vivid2201-dodge-the-swarm-top-the-board\\\/\",\"url\":\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/vivid2201-dodge-the-swarm-top-the-board\\\/\",\"name\":\"Vivid2201: Dodge the Swarm, Top the Board - AndroidAPK\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/vivid2201-dodge-the-swarm-top-the-board\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/vivid2201-dodge-the-swarm-top-the-board\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/Vivid2201-Dodge-the-Swarm-Top-the-Board.webp\",\"datePublished\":\"2026-07-01T08:35:45+00:00\",\"dateModified\":\"2026-07-01T10:04:05+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/#\\\/schema\\\/person\\\/023ca27ae7348fbc0506b1435ff35e78\"},\"description\":\"Play Vivid2201, the one-touch arcade dodger. Fly a lone jet through the alien swarm, score five points a ship and race to the top of the world board.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/vivid2201-dodge-the-swarm-top-the-board\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/vivid2201-dodge-the-swarm-top-the-board\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/vivid2201-dodge-the-swarm-top-the-board\\\/#primaryimage\",\"url\":\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/Vivid2201-Dodge-the-Swarm-Top-the-Board.webp\",\"contentUrl\":\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/Vivid2201-Dodge-the-Swarm-Top-the-Board.webp\",\"width\":703,\"height\":425,\"caption\":\"Vivid2201 Dodge the Swarm, Top the Board\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/vivid2201-dodge-the-swarm-top-the-board\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Vivid2201: Dodge the Swarm, Top the Board\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/\",\"name\":\"AndroidAPK\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/theandroidapk.com\\\/blog\\\/#\\\/schema\\\/person\\\/023ca27ae7348fbc0506b1435ff35e78\",\"name\":\"Chandio\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/bb1d83cd9a251b5ec7cc8322929406bc0477b2934170d5bcc201f69ade815e5d?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/bb1d83cd9a251b5ec7cc8322929406bc0477b2934170d5bcc201f69ade815e5d?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/bb1d83cd9a251b5ec7cc8322929406bc0477b2934170d5bcc201f69ade815e5d?s=96&d=mm&r=g\",\"caption\":\"Chandio\"},\"description\":\"Chandio S. is a skilled and versatile content writer with a passion for crafting impactful stories and engaging articles. With over five years of professional experience, Chandio has a proven track record of producing high-quality content for a diverse range of clients and industries, including technology, health, and lifestyle sectors. Known for their meticulous attention to detail and exceptional research abilities, Chandio has a flair for transforming complex ideas into accessible and enjoyable pieces. As a dedicated wordsmith, Chandio continuously sharpens their writing skills to stay ahead of industry trends and provide clients with fresh, innovative content.\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Vivid2201: Dodge the Swarm, Top the Board - AndroidAPK","description":"Play Vivid2201, the one-touch arcade dodger. Fly a lone jet through the alien swarm, score five points a ship and race to the top of the world board.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/theandroidapk.com\/blog\/vivid2201-dodge-the-swarm-top-the-board\/","og_locale":"en_US","og_type":"article","og_title":"Vivid2201: Dodge the Swarm, Top the Board - AndroidAPK","og_description":"Play Vivid2201, the one-touch arcade dodger. Fly a lone jet through the alien swarm, score five points a ship and race to the top of the world board.","og_url":"https:\/\/theandroidapk.com\/blog\/vivid2201-dodge-the-swarm-top-the-board\/","og_site_name":"AndroidAPK","article_published_time":"2026-07-01T08:35:45+00:00","article_modified_time":"2026-07-01T10:04:05+00:00","og_image":[{"width":703,"height":425,"url":"https:\/\/theandroidapk.com\/blog\/wp-content\/uploads\/2026\/07\/Vivid2201-Dodge-the-Swarm-Top-the-Board.webp","type":"image\/webp"}],"author":"Chandio","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Chandio","Est. reading time":"13 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/theandroidapk.com\/blog\/vivid2201-dodge-the-swarm-top-the-board\/#article","isPartOf":{"@id":"https:\/\/theandroidapk.com\/blog\/vivid2201-dodge-the-swarm-top-the-board\/"},"author":{"name":"Chandio","@id":"https:\/\/theandroidapk.com\/blog\/#\/schema\/person\/023ca27ae7348fbc0506b1435ff35e78"},"headline":"Vivid2201: Dodge the Swarm, Top the Board","datePublished":"2026-07-01T08:35:45+00:00","dateModified":"2026-07-01T10:04:05+00:00","mainEntityOfPage":{"@id":"https:\/\/theandroidapk.com\/blog\/vivid2201-dodge-the-swarm-top-the-board\/"},"wordCount":871,"commentCount":0,"image":{"@id":"https:\/\/theandroidapk.com\/blog\/vivid2201-dodge-the-swarm-top-the-board\/#primaryimage"},"thumbnailUrl":"https:\/\/theandroidapk.com\/blog\/wp-content\/uploads\/2026\/07\/Vivid2201-Dodge-the-Swarm-Top-the-Board.webp","keywords":["alien dodger","endless arcade game","high score game","jet game","online leaderboard game","reflex game","survival arcade game","tap to play game"],"articleSection":["Games"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/theandroidapk.com\/blog\/vivid2201-dodge-the-swarm-top-the-board\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/theandroidapk.com\/blog\/vivid2201-dodge-the-swarm-top-the-board\/","url":"https:\/\/theandroidapk.com\/blog\/vivid2201-dodge-the-swarm-top-the-board\/","name":"Vivid2201: Dodge the Swarm, Top the Board - AndroidAPK","isPartOf":{"@id":"https:\/\/theandroidapk.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/theandroidapk.com\/blog\/vivid2201-dodge-the-swarm-top-the-board\/#primaryimage"},"image":{"@id":"https:\/\/theandroidapk.com\/blog\/vivid2201-dodge-the-swarm-top-the-board\/#primaryimage"},"thumbnailUrl":"https:\/\/theandroidapk.com\/blog\/wp-content\/uploads\/2026\/07\/Vivid2201-Dodge-the-Swarm-Top-the-Board.webp","datePublished":"2026-07-01T08:35:45+00:00","dateModified":"2026-07-01T10:04:05+00:00","author":{"@id":"https:\/\/theandroidapk.com\/blog\/#\/schema\/person\/023ca27ae7348fbc0506b1435ff35e78"},"description":"Play Vivid2201, the one-touch arcade dodger. Fly a lone jet through the alien swarm, score five points a ship and race to the top of the world board.","breadcrumb":{"@id":"https:\/\/theandroidapk.com\/blog\/vivid2201-dodge-the-swarm-top-the-board\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/theandroidapk.com\/blog\/vivid2201-dodge-the-swarm-top-the-board\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/theandroidapk.com\/blog\/vivid2201-dodge-the-swarm-top-the-board\/#primaryimage","url":"https:\/\/theandroidapk.com\/blog\/wp-content\/uploads\/2026\/07\/Vivid2201-Dodge-the-Swarm-Top-the-Board.webp","contentUrl":"https:\/\/theandroidapk.com\/blog\/wp-content\/uploads\/2026\/07\/Vivid2201-Dodge-the-Swarm-Top-the-Board.webp","width":703,"height":425,"caption":"Vivid2201 Dodge the Swarm, Top the Board"},{"@type":"BreadcrumbList","@id":"https:\/\/theandroidapk.com\/blog\/vivid2201-dodge-the-swarm-top-the-board\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/theandroidapk.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Vivid2201: Dodge the Swarm, Top the Board"}]},{"@type":"WebSite","@id":"https:\/\/theandroidapk.com\/blog\/#website","url":"https:\/\/theandroidapk.com\/blog\/","name":"AndroidAPK","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/theandroidapk.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/theandroidapk.com\/blog\/#\/schema\/person\/023ca27ae7348fbc0506b1435ff35e78","name":"Chandio","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/bb1d83cd9a251b5ec7cc8322929406bc0477b2934170d5bcc201f69ade815e5d?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/bb1d83cd9a251b5ec7cc8322929406bc0477b2934170d5bcc201f69ade815e5d?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/bb1d83cd9a251b5ec7cc8322929406bc0477b2934170d5bcc201f69ade815e5d?s=96&d=mm&r=g","caption":"Chandio"},"description":"Chandio S. is a skilled and versatile content writer with a passion for crafting impactful stories and engaging articles. With over five years of professional experience, Chandio has a proven track record of producing high-quality content for a diverse range of clients and industries, including technology, health, and lifestyle sectors. Known for their meticulous attention to detail and exceptional research abilities, Chandio has a flair for transforming complex ideas into accessible and enjoyable pieces. As a dedicated wordsmith, Chandio continuously sharpens their writing skills to stay ahead of industry trends and provide clients with fresh, innovative content."}]}},"_links":{"self":[{"href":"https:\/\/theandroidapk.com\/blog\/wp-json\/wp\/v2\/posts\/16766","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/theandroidapk.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/theandroidapk.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/theandroidapk.com\/blog\/wp-json\/wp\/v2\/users\/8"}],"replies":[{"embeddable":true,"href":"https:\/\/theandroidapk.com\/blog\/wp-json\/wp\/v2\/comments?post=16766"}],"version-history":[{"count":15,"href":"https:\/\/theandroidapk.com\/blog\/wp-json\/wp\/v2\/posts\/16766\/revisions"}],"predecessor-version":[{"id":16796,"href":"https:\/\/theandroidapk.com\/blog\/wp-json\/wp\/v2\/posts\/16766\/revisions\/16796"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/theandroidapk.com\/blog\/wp-json\/wp\/v2\/media\/16767"}],"wp:attachment":[{"href":"https:\/\/theandroidapk.com\/blog\/wp-json\/wp\/v2\/media?parent=16766"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/theandroidapk.com\/blog\/wp-json\/wp\/v2\/categories?post=16766"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/theandroidapk.com\/blog\/wp-json\/wp\/v2\/tags?post=16766"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}