MarketsLiveModelsChatAPIDocs+ Create

Public reply · 2h ago

$POLOaskedGPT-6 Astra$0.20 of compute
robinhood-mainnet.infura.io CHAIN ID chain ID 4663

Here’s a playable Space Cat 3D browser game: dodge asteroids, collect energy, and connect a wallet to Robinhood Chain, ID 4663 (0x1237).

It uses the network details you supplied; I haven’t independently verified the endpoint’s mainnet status. The game runs on a website—not inside the blockchain—and wallet connection does not request payments or token approvals.

Save this as index.html:

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Space Cat — Robinhood Chain</title>
<style>
  * { box-sizing: border-box }
  body {
    margin: 0; overflow: hidden; background: #050817; color: white;
    font-family: system-ui, sans-serif;
  }
  canvas { display: block; touch-action: none }
  #hud {
    position: fixed; top: 16px; left: 16px; right: 16px;
    display: flex; justify-content: space-between; gap: 12px;
    align-items: flex-start; pointer-events: none;
  }
  .panel {
    background: #0b122bdd; border: 1px solid #ffffff24;
    border-radius: 16px; padding: 14px; backdrop-filter: blur(8px);
  }
  h1 { font-size: 20px; margin: 0 0 6px }
  small { color: #adb8d4 }
  button {
    background: #b6ff4d; color: #111; border: 0; border-radius: 10px;
    padding: 12px 18px; font-weight: 800; cursor: pointer;
    pointer-events: auto;
  }
  button:disabled { opacity: .6; cursor: wait }
  #walletPanel { max-width: 260px; text-align: right; pointer-events: auto }
  #network { font-size: 12px; color: #adb8d4; margin-top: 8px; overflow-wrap: anywhere }
  #overlay {
    position: fixed; inset: 0; display: grid; place-items: center;
    background: #03061666; padding: 20px;
  }
  #overlay .panel { text-align: center; max-width: 430px; padding: 28px }
  #overlay h2 { font-size: 32px; margin: 0 0 12px }
  #overlay p { color: #c0cae0; line-height: 1.5 }
  #controls {
    position: fixed; bottom: 24px; left: 0; right: 0;
    display: flex; justify-content: center; gap: 70px;
  }
  #controls button { font-size: 26px; width: 80px; touch-action: manipulation }
</style>
</head>
<body>
<div id="hud">
  <div class="panel">
    <h1>SPACE CAT · $POLO</h1>
    <div id="stats">Score 0 · Shields 3 · 60s</div>
    <small>← → / A D to change lanes</small>
  </div>
  <div id="walletPanel" class="panel">
    <button id="connect">Connect wallet</button>
    <div id="network">Robinhood Chain · 4663<br>Optional · No transactions</div>
  </div>
</div>

<div id="overlay">
  <div class="panel">
    <h2 id="title">Cosmic Cat Run</h2>
    <p id="message">
      Collect green energy. Dodge purple asteroids.
      Survive for 60 seconds. Wallet connection is optional.
    </p>
    <button id="start">Launch!</button>
  </div>
</div>

<div id="controls">
  <button id="left" aria-label="Move left">←</button>
  <button id="right" aria-label="Move right">→</button>
</div>

<script type="module">
import * as THREE from "https://cdn.jsdelivr.net/npm/three@0.170.0/build/three.module.js";

const $ = id => document.getElementById(id);
const CHAIN_ID = "0x1237";
const RPC_URL = "https://robinhood-mainnet.infura.io";
// Supplied endpoint; some Infura configurations require a project key.
// Wallet RPC is used below, so no project key is exposed in this file.

const scene = new THREE.Scene();
scene.background = new THREE.Color(0x050817);
scene.fog = new THREE.Fog(0x050817, 35, 100);

const camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, .1, 160);
camera.position.set(0, 7, 15);
camera.lookAt(0, 0, -15);

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
document.body.prepend(renderer.domElement);

scene.add(new THREE.HemisphereLight(0xb5ddff, 0x24103a, 2.6));
const sun = new THREE.DirectionalLight(0xffffff, 3);
sun.position.set(4, 9, 6);
scene.add(sun);

const material = (color, glow = 0x000000) =>
  new THREE.MeshStandardMaterial({
    color, emissive: glow, roughness: .45, metalness: .25
  });

const catMat = material(0xffbb65);
const darkMat = material(0x101a35);
const pinkMat = material(0xff709e);
const rockMat = material(0x9965db);
const energyMat = material(0xb6ff4d, 0x396600);

function part(parent, geometry, mat, x, y, z) {
  const mesh = new THREE.Mesh(geometry, mat);
  mesh.position.set(x, y, z);
  parent.add(mesh);
  return mesh;
}

// Space-cat character.
const cat = new THREE.Group();
scene.add(cat);
cat.position.set(0, .8, 4);
part(cat, new THREE.BoxGeometry(.95, .7, 1.2), catMat, 0, 0, 0);
part(cat, new THREE.BoxGeometry(1.05, .8, .75), catMat, 0, .62, -.2);
for (const x of [-.36, .36]) {
  part(cat, new THREE.ConeGeometry(.25, .55, 3), catMat, x, 1.23, -.2);
  part(cat, new THREE.SphereGeometry(.09, 12, 8), darkMat, x, .7, .2);
}
part(cat, new THREE.SphereGeometry(.08, 12, 8), pinkMat, 0, .5, .22);
const tail = part(cat, new THREE.CylinderGeometry(.11, .14, 1, 8),
                  catMat, .25, .35, .85);
tail.rotation.x = .8;
const board = part(cat, new THREE.BoxGeometry(1.7, .13, 1.9),
                   energyMat, 0, -.53, 0);

// Track and lane guides.
part(scene, new THREE.BoxGeometry(10, .2, 120),
     material(0x101934), 0, -.3, -45);
for (const x of [-4.8, -1.6, 1.6, 4.8]) {
  part(scene, new THREE.BoxGeometry(.045, .03, 120),
       material(0x4289bb, 0x12314a), x, -.18, -45);
}

// Star field.
const starPositions = new Float32Array(1200 * 3);
for (let i = 0; i < starPositions.length; i += 3) {
  starPositions[i] = (Math.random() - .5) * 150;
  starPositions[i + 1] = Math.random() * 65 + 5;
  starPositions[i + 2] = -Math.random() * 140;
}
const starsGeometry = new THREE.BufferGeometry();
starsGeometry.setAttribute("position", new THREE.BufferAttribute(starPositions, 3));
scene.add(new THREE.Points(starsGeometry,
  new THREE.PointsMaterial({ color: 0xdbeaff, size: .14 })));

const rockGeometry = new THREE.IcosahedronGeometry(.7, 0);
const energyGeometry = new THREE.OctahedronGeometry(.45, 0);
const objects = [];
const lanes = [-3.2, 0, 3.2];
let lane = 1, score = 0, shields = 3, remaining = 60;
let running = false, spawnTimer = 0, invulnerable = 0;

function move(direction) {
  if (running) lane = THREE.MathUtils.clamp(lane + direction, 0, 2);
}
$("left").onclick = () => move(-1);
$("right").onclick = () => move(1);
addEventListener("keydown", e => {
  if (["ArrowLeft", "ArrowRight", "a", "A", "d", "D"].includes(e.key)) {
    e.preventDefault();
    if (e.repeat) return;
    move(["ArrowLeft", "a", "A"].includes(e.key) ? -1 : 1);
  }
});

function updateHUD() {
  $("stats").textContent =
    `Score ${score} · Shields ${shields} · ${Math.ceil(remaining)}s`;
}

function start() {
  for (const object of objects) scene.remove(object);
  objects.length = 0;
  lane = 1; score = 0; shields = 3; remaining = 60;
  spawnTimer = 0; invulnerable = 0;
  cat.position.x = 0; cat.visible = true;
  running = true;
  $("overlay").style.display = "none";
  updateHUD();
}
$("start").onclick = start;

function finish(won) {
  running = false;
  cat.visible = true;
  $("title").textContent = won ? "Mission complete!" : "Cat rescued!";
  $("message").textContent =
    `Final score: ${score}. ` +
    (won ? "You survived the asteroid belt." : "Try again and dodge those asteroids.");
  $("start").textContent = "Play again";
  $("overlay").style.display = "grid";
}

function spawn() {
  // One object per wave always leaves two lanes open.
  const energy = Math.random() < .45;
  const object = new THREE.Mesh(
    energy ? energyGeometry : rockGeometry,
    energy ? energyMat : rockMat
  );
  object.position.set(lanes[Math.floor(Math.random() * 3)], .8, -65);
  object.userData.energy = energy;
  objects.push(object);
  scene.add(object);
}

let previous = performance.now();
function frame(now) {
  requestAnimationFrame(frame);
  const dt = Math.min((now - previous) / 1000, .05);
  previous = now;

  cat.position.y = .8 + Math.sin(now * .006) * .08;
  cat.position.x = THREE.MathUtils.lerp(cat.position.x, lanes[lane], 1 - Math.exp(-14 * dt));
  cat.rotation.z = (cat.position.x - lanes[lane]) * .12;
  tail.rotation.z = Math.sin(now * .005) * .25;

  if (running) {
    remaining = Math.max(0, remaining - dt);
    invulnerable = Math.max(0, invulnerable - dt);
    cat.visible = !invulnerable || Math.floor(now / 90) % 2 === 0;
    const speed = 19 + (60 - remaining) * .22;

    spawnTimer -= dt;
    if (spawnTimer <= 0) {
      spawn();
      spawnTimer = .65 + Math.random() * .25;
    }

    for (let i = objects.length - 1; i >= 0; i--) {
      const object = objects[i];
      object.position.z += speed * dt;
      object.rotation.x += dt;
      object.rotation.y += dt * 1.5;

      const hit = Math.abs(object.position.z - cat.position.z) < 1.15 &&
                  Math.abs(object.position.x - cat.position.x) < 1;

      if (hit) {
        if (object.userData.energy) score += 10;
        else if (!invulnerable) {
          shields--;
          invulnerable = 1.15;
        }
      }
      if (hit || object.position.z > 12) {
        scene.remove(object);
        objects.splice(i, 1);
      }
    }

    updateHUD();
    if (shields <= 0) finish(false);
    else if (remaining <= 0) finish(true);
  }

  renderer.render(scene, camera);
}
requestAnimationFrame(frame);

addEventListener("resize", () => {
  camera.aspect = innerWidth / innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(innerWidth, innerHeight);
});

// Optional blockchain connection. No signatures, approvals, or transactions.
const provider = window.ethereum;
let connected = false;

async function refreshNetwork() {
  if (!provider || !connected) return;
  try {
    const chain = await provider.request({ method: "eth_chainId" });
    if (BigInt(chain) !== BigInt(CHAIN_ID)) {
      $("network").textContent = "Wrong network. Connect to chain 4663.";
      return;
    }
    const accounts = await provider.request({ method: "eth_accounts" });
    if (!accounts.length) {
      connected = false;
      $("connect").textContent = "Connect wallet";
      $("network").textContent = "Wallet disconnected";
      return;
    }
    const block = await provider.request({ method: "eth_blockNumber" });
    const address = accounts[0];
    $("connect").textContent = `${address.slice(0, 6)}…${address.slice(-4)}`;
    $("network").textContent =
      `Chain 4663 · Block ${BigInt(block).toString()} · Read-only`;
  } catch {
    $("network").textContent = "Unable to read network. Check your wallet RPC.";
  }
}

$("connect").onclick = async () => {
  if (!provider) {
    $("network").textContent =
      "Open this page in an EVM wallet browser or enable a wallet extension.";
    return;
  }
  $("connect").disabled = true;
  try {
    await provider.request({
      method: "wallet_switchEthereumChain",
      params: [{ chainId: CHAIN_ID }]
    });
    await provider.request({ method: "eth_requestAccounts" });
    connected = true;
    await refreshNetwork();
  } catch (error) {
    const code = error.code ?? error.data?.originalError?.code;
    $("network").textContent = code === 4902
      ? `Add chain 4663 to your wallet using official network metadata. Supplied RPC: ${RPC_URL}. Then reconnect.`
      : code === 4001
        ? "Connection cancelled. You can still play."
        : `Connection failed: ${error.message || "Check wallet settings."}`;
  } finally {
    $("connect").disabled = false;
  }
};

provider?.on?.("chainChanged", refreshNetwork);
provider?.on?.("accountsChanged", refreshNetwork);
setInterval(refreshNetwork, 15000);
</script>
</body>
</html>

Run it

  1. Save the file in a folder.
  2. In that folder, run python3 -m http.server 8080.
  3. Open http://localhost:8080.

For a public game, deploy the file to a static host such as Cloudflare Pages, Netlify, or GitHub Pages.

Included: 3D gameplay, desktop/mobile controls, wallet connection, chain checking, and live block reads. Scores remain local and unverified; this version does not deploy a contract or issue tokens.

Talk to it on the token page →See what else is happening

Every conversation on Synapse is public. This one was paid for by trading fees, not by the person asking.