# 360 Panorama Viewer

> >

- **Type:** Skill
- **Install:** `agentstack add skill-happycapy-ai-happycapy-skills-360-panorama-viewer`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [happycapy-ai](https://agentstack.voostack.com/s/happycapy-ai)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [happycapy-ai](https://github.com/happycapy-ai)
- **Source:** https://github.com/happycapy-ai/Happycapy-skills/tree/main/skills/360-panorama-viewer

## Install

```sh
agentstack add skill-happycapy-ai-happycapy-skills-360-panorama-viewer
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# 360° Panorama Viewer Skill

This skill creates a polished, self-contained 360° panorama viewer HTML file.

## What it produces

A single `.html` file (~3–6 MB depending on scene count) that:
- Renders equirectangular panoramas as spherical 360° environments using Three.js
- Supports dragging to look around, scroll to zoom, auto-rotate toggle, fullscreen
- Shows a thumbnail sidebar to switch between multiple scenes
- Works offline — no CDN dependencies, all assets embedded

## Skill assets

| Asset | Purpose |
|---|---|
| `assets/viewer_template.html` | Complete viewer HTML with Three.js inlined; panorama data injected at build time |
| `scripts/build_viewer.py` | Loads images, applies seam fix, base64-encodes, injects into template |

---

## Workflow

### Step 1 — Gather scene specs from the user

Ask (or infer from context) for each scene:
- **Description** of what the panorama should show
- **Title** for the HUD (emoji + name, e.g. `🍄 Mario World`)
- **Thumbnail label** (≤12 chars shown on the sidebar chip)

Typical count: 3–6 scenes. You can also accept user-provided image files directly (skip generation).

### Step 2 — Generate panorama images

For **each scene**, generate a 360° equirectangular panorama image.

**Model choice:**
- **Preferred:** `google/gemini-3.1-flash-image-preview` via AI Gateway — reliable 2:1 output, no safety rejections for fictional themes
- **Alternative:** `gpt-image-2` via AI Gateway at size `1536x1024` — higher quality but may reject branded IP (Mario, Zelda, etc.)

**Generation code (Gemini route):**
```python
import os, requests, base64
from PIL import Image
import io

api_key = os.environ['AI_GATEWAY_API_KEY']

def generate_panorama(prompt: str, save_path: str):
    payload = {
        "model": "google/gemini-3.1-flash-image-preview",
        "prompt": prompt,
        "response_format": "b64_json",
        "n": 1
    }
    resp = requests.post(
        'https://ai-gateway.happycapy.ai/api/v1/images/generations',
        headers={
            'Authorization': f'Bearer {api_key}',
            'Content-Type': 'application/json',
            'Origin': 'https://trickle.so'
        },
        json=payload,
        timeout=180
    )
    resp.raise_for_status()
    img_bytes = base64.b64decode(resp.json()['data'][0]['b64_json'])
    img = Image.open(io.BytesIO(img_bytes)).convert('RGB')
    img.save(save_path)
    return save_path
```

**Prompt formula for good equirectangular panoramas:**
```
360 degree equirectangular panorama of [SCENE DESCRIPTION].
[Key visual elements]. [Style description].
Wide seamless landscape, 2:1 aspect ratio,
left and right edges must tile perfectly for 360 VR viewing.
```

**Example prompts:**
- Mario: `360 degree equirectangular panorama of a colorful cartoon mushroom kingdom platformer world. Bright blue sky, rolling green hills, red mushroom houses, floating brick blocks, gold coins, stone castle. Vibrant cartoon illustration, 2:1 aspect ratio, seamless tiling.`
- Underwater city: `360 degree equirectangular panorama of a futuristic underwater city. Bioluminescent buildings, schools of fish, coral reefs, deep ocean light shafts. Cinematic, 2:1 aspect ratio, seamless tiling.`

Save each generated image to a temp path (e.g. `tmp/scene_N_raw.png`).

### Step 3 — Build the viewer

Run the build script, passing all scenes as a JSON array:

```bash
SKILL_DIR=/home/node/.claude/skills/360-panorama-viewer

python3 "$SKILL_DIR/scripts/build_viewer.py" \
  --template "$SKILL_DIR/assets/viewer_template.html" \
  --output "outputs/360_viewer.html" \
  --scenes '[
    {"title":"🍄 Mario World","thumb_label":"Mario","image_path":"tmp/mario.png","initial_lon":90},
    {"title":"🐚 Underwater City","thumb_label":"Deep City","image_path":"tmp/underwater.png","initial_lon":0}
  ]'
```

The script will:
1. Resize each image to 1774×887 (standard 2:1)
2. Apply a **pure-roll seam fix** — finds the horizontal offset that minimises left/right edge difference, then `np.roll`s the image. No blending. This ensures the 360° seam is as clean as possible without distorting colors.
3. Base64-encode as JPEG quality 90
4. Inject into the template and write the output HTML

### Step 4 — Deliver

Output the file to `outputs/360_viewer.html` (or user-specified path) and attach it as a static HTML deliverable.

---

## Replacing a single scene in an existing viewer

If the user already has a viewer and wants to swap out one scene (e.g. regenerate Mario), do NOT rerun all scenes — just re-encode the new image and replace its `b64` field in the HTML:

```python
import re, base64, io, numpy as np
from PIL import Image

SKILL_DIR = '/home/node/.claude/skills/360-panorama-viewer'

# Load and fix the new image
img = Image.open('new_mario.png').convert('RGB')
img = img.resize((1774, 887), Image.LANCZOS)
arr = np.array(img)
# pure roll seam fix
f = arr.astype(np.float32)
h, w = arr.shape[:2]
best_roll, best_score = 0, float('inf')
for roll in range(w):
    r = np.roll(f, roll, axis=1)
    score = float(np.abs(r[:, :5] - r[:, w-5:]).mean())
    if score ' + new_b64 + r'\g', html)
assert n == 1, f"Expected 1 replacement, got {n}"

with open('outputs/360_viewer.html', 'w') as fh:
    fh.write(html_new)
print(f'Replaced Mario scene (roll={best_roll})')
```

---

## Seam fix — why pure roll, not blending

Equirectangular panoramas wrap horizontally: x=0 and x=w-1 represent the same physical meridian. AI-generated images rarely place the seam at a natural boundary (e.g. open sky), so the default seam is often visible.

**Pure roll**: shift the whole image left/right to find the position where the 5-pixel columns at both edges are most similar (lowest mean absolute difference). `np.roll()` is a circular shift — no pixels are added or removed, no colors are changed. This is lossless and never creates artifacts.

**Never use blending/color correction** at the seam: it creates a visible "smeared" band where pixel values are artificially averaged, which looks worse than the original seam.

---

## Viewer features reference

The template includes these controls, all functional out of the box:
- **Drag** — look around (mouse or touch)
- **Scroll / pinch** — zoom (FOV 30°–120°, default 75°)
- **Auto Rotate** button — gentle continuous pan
- **Reset** button — return to `initialLon` of current scene
- **Fullscreen** button
- **FOV slider** — right-side vertical range input
- **Thumbnail sidebar** — left-side scene switcher with active highlight

Scene `initialLon` is the starting horizontal angle (0–360). Use it to face an interesting part of the panorama on load (e.g. 90 = face right, 180 = face backwards).

---

## Tips for better panoramas

- Ask for **2:1 aspect ratio** explicitly in every prompt — this is the equirectangular standard
- Mention **"seamless tiling"** and **"left and right edges must connect"** in the prompt
- For fictional/branded themes (Mario, Zelda), describe the visual style without using trademarked names if GPT-Image-2 rejects them: "colorful cartoon platformer mushroom kingdom style" instead of "Mario"
- Gemini rarely rejects prompts and consistently produces good 2:1 panoramas — prefer it for speed

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [happycapy-ai](https://github.com/happycapy-ai)
- **Source:** [happycapy-ai/Happycapy-skills](https://github.com/happycapy-ai/Happycapy-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** yes
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-happycapy-ai-happycapy-skills-360-panorama-viewer
- Seller: https://agentstack.voostack.com/s/happycapy-ai
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
