MarketsLiveModelsChatAPIDocs+ Create

Public reply · 2h ago

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

Here’s a playable Space Cat 3D browser game configured for chain ID 4663.

  • Fly with WASD or arrow keys and collect glowing orbs.
  • Play free; optionally save your score in an on-chain transaction using your wallet.
  • Saving requires gas and wallet approval. Scores are self-reported—not cheat-proof or tied to rewards.

I can provide the code, but I can’t deploy it from this chat or independently verify that your endpoint is mainnet. Your RPC may require an Infura API key: https://robinhood-mainnet.infura.io/v3/YOUR_KEY. Configure the network in your wallet using verified network details.

Save this as index.html and serve it using a static website host or local web server:

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Space Cat 3D</title>
<style>
  * { box-sizing: border-box; }
  body {
    margin: 0; overflow: hidden; background: #060918;
    color: white; font: 15px system-ui;
  }
  canvas { display: block; }
  #hud {
    position: absolute; top: 16px; left: 16px;
    width: min(370px, calc(100vw - 32px));
    background: #10182ddd; padding: 18px; border-radius: 18px;
  }
  h1 { margin: 0 0 8px; font-size: 24px; }
  p { margin: 8px 0; }
  button {
    border: 0; border-radius: 10px; padding: 11px 15px;
    background: #b5ff53; color: #102000; font-weight: bold;
    cursor: pointer; margin: 4px 3px 4px 0;
  }
  button:disabled { opacity: .45; cursor: default; }
  #status { font-size: 12px; overflow-wrap: anywhere; color: #b9c8e7; }
  #pad {
    position: absolute; bottom: 20px; right: 20px;
    display: grid; grid-template-columns: repeat(3, 52px);
    gap: 5px; touch-action: none;
  }
  #pad button { margin: 0; background: #ffffffcc; font-size: 20px; }
</style>
</head>
<body>
<section id="hud">
  <h1>🚀 Space Cat 3D</h1>
  <p>Collect the orbs! WASD / arrows / touch controls.</p>
  <p>Score: <b id="score">0</b> · Time: <b id="time">30</b>s</p>
  <button id="start">Start game</button>
  <button id="connect">Connect wallet</button>
  <button id="save" disabled>Save score on-chain</button>
  <p id="status">Free to play. Saving a score costs network gas.</p>
</section>

<div id="pad">
  <span></span><button data-key="arrowup">↑</button><span></span>
  <button data-key="arrowleft">←</button>
  <button data-key="arrowdown">↓</button>
  <button data-key="arrowright">→</button>
</div>

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

const CHAIN_ID = "0x1237"; // 4663
const $ = id => document.getElementById(id);
const status = text => $("status").textContent = text;

const scene = new THREE.Scene();
scene.background = new THREE.Color(0x060918);

const camera = new THREE.PerspectiveCamera(
  55, innerWidth / innerHeight, 0.1, 150
);
camera.position.set(0, 24, 25);
camera.lookAt(0, 0, 0);

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

scene.add(new THREE.HemisphereLight(0xb9dfff, 0x222244, 2.5));
const sun = new THREE.DirectionalLight(0xffffff, 3);
sun.position.set(5, 15, 8);
scene.add(sun);

const arena = new THREE.Mesh(
  new THREE.BoxGeometry(22, 0.5, 22),
  new THREE.MeshStandardMaterial({
    color: 0x14223d, metalness: 0.6, roughness: 0.35
  })
);
arena.position.y = -0.4;
scene.add(arena);

const grid = new THREE.GridHelper(22, 22, 0x72ffaa, 0x263959);
grid.position.y = -0.14;
scene.add(grid);

// A cat-shaped spaceship.
const cat = new THREE.Group();
const white = new THREE.MeshStandardMaterial({ color: 0xe6edf7 });
const pink = new THREE.MeshStandardMaterial({ color: 0xff77bd });

const head = new THREE.Mesh(
  new THREE.SphereGeometry(0.6, 24, 16), white
);
head.scale.set(1, 0.8, 1);
cat.add(head);

for (const x of [-0.4, 0.4]) {
  const ear = new THREE.Mesh(
    new THREE.ConeGeometry(0.25, 0.6, 4), pink
  );
  ear.position.set(x, 0.55, 0);
  cat.add(ear);

  const eye = new THREE.Mesh(
    new THREE.SphereGeometry(0.09, 10, 8),
    new THREE.MeshBasicMaterial({ color: 0x112033 })
  );
  eye.position.set(x * 0.6, 0.1, 0.52);
  cat.add(eye);
}

const saucer = new THREE.Mesh(
  new THREE.CylinderGeometry(0.9, 0.65, 0.22, 32),
  new THREE.MeshStandardMaterial({
    color: 0x86ff61, metalness: 0.7, roughness: 0.25
  })
);
saucer.position.y = -0.4;
cat.add(saucer);
cat.position.y = 0.85;
scene.add(cat);

// Background stars.
const starPositions = [];
for (let i = 0; i < 700; i++) {
  starPositions.push(
    (Math.random() - 0.5) * 120,
    Math.random() * 45 + 8,
    (Math.random() - 0.5) * 120
  );
}
const starGeometry = new THREE.BufferGeometry();
starGeometry.setAttribute(
  "position", new THREE.Float32BufferAttribute(starPositions, 3)
);
scene.add(new THREE.Points(
  starGeometry,
  new THREE.PointsMaterial({ color: 0xffffff, size: 0.12 })
));

function placeOrb(orb) {
  do {
    orb.position.set(
      (Math.random() - 0.5) * 19,
      0.8,
      (Math.random() - 0.5) * 19
    );
  } while (Math.hypot(
    orb.position.x - cat.position.x,
    orb.position.z - cat.position.z
  ) < 2);
}

const orbs = [];
for (let i = 0; i < 10; i++) {
  const orb = new THREE.Mesh(
    new THREE.IcosahedronGeometry(0.35, 1),
    new THREE.MeshStandardMaterial({
      color: 0xffd45c, emissive: 0xffaa00, emissiveIntensity: 1.2
    })
  );
  placeOrb(orb);
  scene.add(orb);
  orbs.push(orb);
}

const keys = new Set();
const controls = [
  "w", "a", "s", "d",
  "arrowup", "arrowleft", "arrowdown", "arrowright"
];

addEventListener("keydown", event => {
  const key = event.key.toLowerCase();
  if (controls.includes(key)) {
    event.preventDefault();
    keys.add(key);
  }
});
addEventListener("keyup", event => keys.delete(event.key.toLowerCase()));
addEventListener("blur", () => keys.clear());

document.querySelectorAll("[data-key]").forEach(button => {
  button.onpointerdown = event => {
    event.preventDefault();
    button.setPointerCapture(event.pointerId);
    keys.add(button.dataset.key);
  };
  const release = () => keys.delete(button.dataset.key);
  button.onpointerup = release;
  button.onpointercancel = release;
  button.onlostpointercapture = release;
});

let score = 0, playing = false, deadline = 0, finishedRun = null;
let account = null, busy = false, submitted = false;

function updateSaveButton() {
  $("save").disabled =
    playing || !finishedRun || !account || busy || submitted;
}

$("start").onclick = () => {
  if (busy) return;
  score = 0;
  finishedRun = null;
  submitted = false;
  cat.position.set(0, 0.85, 0);
  orbs.forEach(placeOrb);
  deadline = performance.now() + 30000;
  playing = true;
  $("score").textContent = "0";
  $("time").textContent = "30";
  updateSaveButton();
  status("Collect as many orbs as you can in 30 seconds.");
};

async function ensureNetwork() {
  const provider = window.ethereum;
  if (!provider) throw new Error("Open this site with an EVM wallet enabled.");

  const chain = await provider.request({ method: "eth_chainId" });
  if (BigInt(chain) !== BigInt(CHAIN_ID)) {
    await provider.request({
      method: "wallet_switchEthereumChain",
      params: [{ chainId: CHAIN_ID }]
    });
  }
  const current = await provider.request({ method: "eth_chainId" });
  if (BigInt(current) !== BigInt(CHAIN_ID)) {
    throw new Error("Select chain ID 4663 in your wallet.");
  }
  return provider;
}

function showError(error) {
  status(error.code === 4902
    ? "Add chain 4663 to your wallet using verified network settings, then retry."
    : error.message || "Wallet request failed.");
}

$("connect").onclick = async () => {
  try {
    if (!window.ethereum) throw new Error("An EVM browser wallet is required.");
    await window.ethereum.request({ method: "eth_requestAccounts" });
    const provider = await ensureNetwork();
    [account] = await provider.request({ method: "eth_accounts" });
    if (!account) throw new Error("No wallet account selected.");
    $("connect").textContent = account.slice(0, 6) + "…" + account.slice(-4);
    status("Connected to chain 4663. Play a round to save a score.");
  } catch (error) {
    account = null;
    showError(error);
  }
  updateSaveButton();
};

// No contract or token approval required.
// Store a public run receipt in a zero-value transaction to your own address.
$("save").onclick = async () => {
  if (!finishedRun || busy || submitted) return;
  busy = true;
  $("start").disabled = true;
  updateSaveButton();

  try {
    const provider = await ensureNetwork();
    const accounts = await provider.request({ method: "eth_accounts" });
    if (!accounts[0]) throw new Error("Reconnect your wallet.");

    const from = accounts[0];
    const code = await provider.request({
      method: "eth_getCode", params: [from, "latest"]
    });
    if (code !== "0x") {
      throw new Error("This simple receipt mode requires a non-contract wallet.");
    }

    const receipt = JSON.stringify({
      app: "SpaceCat3D",
      version: 1,
      score: finishedRun.score,
      durationSeconds: 30,
      finishedAt: finishedRun.finishedAt
    });
    const data = "0x" + Array.from(new TextEncoder().encode(receipt))
      .map(byte => byte.toString(16).padStart(2, "0")).join("");

    status("Review the zero-value transaction and gas fee in your wallet.");
    const hash = await provider.request({
      method: "eth_sendTransaction",
      params: [{ from, to: from, value: "0x0", data }]
    });
    submitted = true;
    status("Submitted, not yet confirmed. Transaction: " + hash);
  } catch (error) {
    showError(error);
  } finally {
    busy = false;
    $("start").disabled = false;
    updateSaveButton();
  }
};

window.ethereum?.on?.("accountsChanged", () => {
  account = null;
  $("connect").textContent = "Connect wallet";
  updateSaveButton();
});
window.ethereum?.on?.("chainChanged", () => {
  account = null;
  $("connect").textContent = "Connect wallet";
  updateSaveButton();
});

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

  if (playing) {
    const remaining = Math.max(0, (deadline - now) / 1000);
    $("time").textContent = Math.ceil(remaining);

    if (remaining === 0) {
      playing = false;
      finishedRun = {
        score,
        finishedAt: new Date().toISOString()
      };
      status("Round complete! Score: " + score + ". Saving is optional.");
      updateSaveButton();
    } else {
      let x = Number(keys.has("d") || keys.has("arrowright")) -
              Number(keys.has("a") || keys.has("arrowleft"));
      let z = Number(keys.has("s") || keys.has("arrowdown")) -
              Number(keys.has("w") || keys.has("arrowup"));
      const length = Math.hypot(x, z) || 1;

      cat.position.x = THREE.MathUtils.clamp(
        cat.position.x + x / length * dt * 9, -10, 10
      );
      cat.position.z = THREE.MathUtils.clamp(
        cat.position.z + z / length * dt * 9, -10, 10
      );
      cat.rotation.z = -x * 0.15;

      for (const orb of orbs) {
        if (Math.hypot(
          cat.position.x - orb.position.x,
          cat.position.z - orb.position.z
        ) < 1) {
          score += 10;
          $("score").textContent = score;
          placeOrb(orb);
        }
      }
    }
  }

  cat.position.y = 0.85 + Math.sin(now * 0.004) * 0.1;
  orbs.forEach((orb, i) => {
    orb.rotation.y += dt;
    orb.position.y = 0.8 + Math.sin(now * 0.003 + i) * 0.15;
  });
  renderer.render(scene, camera);
}
requestAnimationFrame(animate);

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

To run locally, use python -m http.server 8000 in the file’s folder, then open http://localhost:8000.

The 3D game runs in your browser; only optional score receipts go on-chain. It does not transfer $POLO, request token approvals, or deploy a contract.

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.