3d-logo
Create a 3D spinning coin/medal effect from any logo image using React Three Fiber.
Skill by hasuwini77
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/hasuwini77-3d-logo
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: design, frontend
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md and code. Checked Oct 26, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
Network hosts it talks to 1
codeload.github.com· found in install command
Files it touches
~/.claude/skills/3d-logo
What's inside
- Skill name
3d-logo- Description
- Create a 3D spinning coin/medal effect from any logo image using React Three Fiber. Auto-detects the logo's outline, removes solid backgrounds (white, black or any flat colour), builds a chrome-rimmed coin that follows the exact logo shape, and adds environment reflections. Use when the user wants a 3D logo, spinning logo, rotating logo, coin effect, medal effect, logo animation, or mentions making a logo "3D" or "spin." Also trigger when user says "3d-logo", "spinning coin", "logo coin", "3D badge", or wants to turn a flat image into a premium 3D rotating element.
- License
- MIT
- Instructions
- 692 lines in SKILL.md · view on GitHub
Before installing
- Instructions call the network
Agent Skills spec issues
- body: 692 lines (spec recommends under 500)
Files (1)
- SKILL.md
SKILL.md (first lines)
# 3D Logo Spinner
Generate a self-contained React component that renders any logo as a premium 3D spinning coin/medal with chrome edges and environment reflections.
## What it produces
A `SpinningLogo3D.tsx` component that:
- Takes any logo image (PNG, JPG, SVG rasterized) and renders it as a 3D coin
- Auto-removes solid backgrounds (white, black or any flat colour) by converting to transparency at runtime
- Extracts the logo's exact perimeter from the alpha channel
- Builds a chrome rim that follows the logo's actual outline shape
- Renders the logo on BOTH faces, with the back face's outline matching the rim exactly (seen from behind it's the mirror image, as on a real stamped coin)
- Text logos (`HAS_TEXT`) read correctly from BOTH sides while the coin keeps spinning continuously — no yaw snap, no pop, no stepped wall at edge-on
- Adds environment reflections for a premium chrome finish
- Spins smoothly on the Y axis
## Dependencies
The target project needs these packages:
```bash
npm install three @react-three/fiber @react-three/drei
npm install -D @types/three
```
## How to build the component
### Step 1: Identify the logo
Ask the user for the logo path (relative to `public/` or `src/assets/`). Check the file exists. Read it to see the logo visually — this helps you understand the shape.
Check the actual file format:
```bash
file <path-to-logo>
```
The extension may lie (e.g., a `.png` that's actually JPEG). This matters because JPEG has no alpha channel — the transparency must be generated at runtime.
**Does the logo contain text?** Look at the image. If it has letters or numbers, set `HAS_TEXT = true` (ask the user if unsure). The back face is a true mirror image (see 2d), so text would read backwards; `HAS_TEXT` mirrors the back and narrows the whole coin through edge-on (2d) so the one rim reads as the front outline before edge-on and its mirror after — it reads correctly from both sides.
### Step 2: Generate the component
Create `SpinningLogo3D.tsx` using this architecture. The file starts with this two-line attribution header (after a `'use client'` directive if the project needs one, since directives must come first). Keep it in the generated file:
```tsx
// 3D spinning logo coin, generated by 3d-logo-skill
// https://github.com/hasuwini77/3d-logo-skill
```
#### 2a. Dynamic transparency generation and normal map
The logo image likely has a solid background — white, black, or any other flat colour — with no alpha channel. Detect that background by sampling the image's 1px border and taking the median colour; if enough border pixels agree, remove it with a **contiguous** flood fill seeded from the border, so only the background region actually connected to the edge clears — an enclosed same-colour detail inside the logo (the white of an eye, the counter of an "O") is never touched, because the fill can only reach it through connected matching pixels and there's no path in from the border. A border that doesn't agree on one colour (a photo, a gradient, a busy edge) falls back to the old global dark-brightness threshold. Also generate a normal map via Sobel filter for embossed depth on the coin faces. Requires `Vector2` and `LinearSRGBColorSpace` from Three.js in addition to the other imports:
```tsx
const BG_TOLERANCE = 24 // max per-channel diff to count as "the same colour" as the background
const BORDER_MATCH_RATIO = 0.85 // fraction of border pixels that must agree for it to count as solid
interface RGB { r: number; g: number; b: number }
// Median colour of the image's border; null if the border doesn't agree
// (a photo/gradient/busy edge) — caller falls back to the brightness threshold.
function detectSolidBackground(data: Uint8ClampedArray, width: number, height: number): RGB | null {
const pixelAt = (x: number, y: number): RGB => {
const i = (y * width + x) * 4
return { r: data[i], g: data[i + 1], b: data[i + 2] }
}
const border: RGB[] = []
for (let x = 0; x < width; x++) { border.push(pixelAt(x, 0)); border.push(pixelAt(x, height - 1)) }
for (let y = 1; y < height - 1; y++) { border.push(pixelAt(0, y)); border.push(pixelAt(width - 1, y)) }
const median = (vals: number[]) => [...vals].sort((a, b) => a - b)[Math.floor(vals.length / 2)]
const bg: RGB = { r: median(border.map((p) => p.r)), g: median(border.map((p) => p.g)), b: median(border.map((p) => p.b)) }
const diff = (p: RGB) => Math.max(Math.abs(p.r - bg.r), Math.abs(p.g - bg.g), Math.abs(p.b - bg.b))
const matches = border.filter((p) => diff(p) <= BG_TOLERANCE).length
return matches / border.length >= BORDER_MATCH_RATIO ? bg : null
}
// Iterative (explicit stack, never recursive — must handle 1024x1024 fast)
// 4-connected flood fill seeded from every border pixel matching `bg`. Only
// the background CONTIGUOUS with the border is cleared. Near-match pixels
// next to the cleared region get their alpha scaled down for a soft,
// anti-aliased edge instead of a hard white/colour halo.
function removeBackgroundFromEdges(
data: Uint8ClampedArray, width: number, height: number, bg: RGB, tolerance = BG_TOLERANCE,
): void {
const n = width * height
const removed = new Uint8Array(n)
const visited = new Uint8Array(n)
const stack = new Int32Array(n)
let stackLen = 0
const dist = (i: number) => {
const o = i * 4
return Math.max(Math.abs(data[o] - bg.r), Math.abs(data[o + 1] - bg.g), Math.abs(data[o + 2] - bg.b))
}
const visit = (i: number) => {
if (visited[i]) return
visited[i] = 1
if (dist(i) <= tolerance) { removed[i] = 1; stack[stackLen++] = i }
}
for (let x = 0; x < width; x++) { visit(x); visit((height - 1) * width + x) }
for (let y = 0; y < height; y++) { visit(y * width); visit(y * width + width - 1) }
while (stackLen > 0) {
const i = stack[--stackLen]
const x = i % width, y = (i / width) | 0
if (x > 0) visit(i - 1)
if (x < width - 1) visit(i + 1)
if (y > 0) visit(i - width)
if (y < height - 1) visit(i + width)
}
for (let i = 0; i < n; i++) {
const o = i * 4
if (removed[i]) { data[o + 3] = 0; continue }
const x = i % width, y = (i / width) | 0
const nearRemoved =
(x > 0 && removed[i - 1] === 1) || (x < width - 1 && removed[i + 1] === 1) ||
(y > 0 && removed[i - width] === 1) || (y < height - 1 && removed[i + width] === 1)
if (!nearRemoved) continue
const d = dist(i)
if (d > tolerance && d < 2 * tolerance) data[o + 3] = Math.round(data[o + 3] * ((d - tolerance) / tolerance))
}
}
function useLogoTextures(logoPath: string, threshold = 18) {From hasuwini77/3d-logo-skill, MIT.
Signals
- Stars
- 5
- Forks
- 1
- Open issues
- 4
- Last commit (this folder)
- Oct 3, 2026 (25 days ago)
- Last release
- Oct 4, 2026 (24 days ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/hasuwini77/3d-logo-skill
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
Cite this page
3d-logo skill. Agent Mart. https://agentmart.dev/skills/hasuwini77-3d-logo (last updated 2026-10-24)