Skip to content
Back to skills

Retro Pixel

ASecurity

Create retro and pixel art visual effects as React components for shadcn/ui projects. CRT scanlines, dithering, 8-bit aesthetics, VHS glitch, pixel art rendering, chiptune, ASCII art, demoscene effects, and nostalgic computing vibes. Use when: user wants retro effects, CRT look, pixel art, glitch effects, VHS aesthetic, or vintage computing style.

  • 6 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added June 1, 2026
ai-agentstypescriptreactnextjsnodeperformance

Works with

  • cli

Security analysis

A92/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned June 1, 2026

npx -y skills add KevinZai/commander --skill retro-pixel --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Retro Pixel?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Retro Pixel
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/kevinzai-retro-pixel/badge)](https://www.skillsdirectory.com/skills/kevinzai-retro-pixel)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: retro-pixel
description: "Create retro and pixel art visual effects as React components for shadcn/ui projects. CRT scanlines, dithering, 8-bit aesthetics, VHS glitch, pixel art rendering, chiptune, ASCII art, demoscene effects, and nostalgic computing vibes. Use when: user wants retro effects, CRT look, pixel art, glitch effects, VHS aesthetic, or vintage computing style."
---

# Retro Pixel Skill

Create retro/pixel art effect components for **Next.js + shadcn/ui + Tailwind CSS + TypeScript** projects.

All output MUST be:
- React functional components with TypeScript
- Styled with Tailwind + shadcn CSS variables
- Composable with shadcn/ui components
- SSR-safe with `"use client"` where needed
- Providing fallback for `prefers-reduced-motion`

## Effect Recipes

### 1. CRT Monitor Effect
```tsx
"use client"
import { cn } from "@/lib/utils"

export function CRTScreen({ children, className }: { children: React.ReactNode; className?: string }) {
  return (
    <div className={cn("relative overflow-hidden rounded-lg", className)}>
      {children}
      {/* Scanlines */}
      <div
        className="pointer-events-none absolute inset-0 z-10"
        style={{
          background: "repeating-linear-gradient(0deg, transparent, transparent 1px, rgba(0,0,0,0.15) 1px, rgba(0,0,0,0.15) 2px)",
          backgroundSize: "100% 2px",
        }}
      />
      {/* Screen curvature */}
      <div
        className="pointer-events-none absolute inset-0 z-10"
        style={{
          boxShadow: "inset 0 0 80px rgba(0,0,0,0.5), inset 0 0 20px rgba(0,0,0,0.3)",
          borderRadius: "inherit",
        }}
      />
      {/* Phosphor flicker */}
      <div className="pointer-events-none absolute inset-0 z-10 animate-[crt-flicker_0.15s_infinite]" />
    </div>
  )
}

// tailwind.config.ts keyframe:
// "crt-flicker": { "0%,100%": { opacity: "0.98" }, "50%": { opacity: "1" } }
```

### 2. VHS / Tape Effect
```tsx
// Canvas-based post-processing:
// 1. Render content to offscreen canvas
// 2. Apply effects:
const applyVHS = (ctx: CanvasRenderingContext2D, width: number, height: number) => {
  const imageData = ctx.getImageData(0, 0, width, height)
  const data = imageData.data

  // Color bleed: shift red channel horizontally
  for (let y = 0; y < height; y++) {
    for (let x = 0; x < width - 3; x++) {
      const i = (y * width + x) * 4
      data[i] = data[i + 12] // shift red 3px right
    }
  }

  // Tracking lines: random horizontal displacement
  const trackingY = Math.floor(Math.random() * height)
  const offset = Math.floor(Math.random() * 10) - 5
  // Shift entire row by offset pixels

  // Static noise: random white pixels
  for (let i = 0; i < data.length; i += 4) {
    if (Math.random() < 0.005) {
      data[i] = data[i+1] = data[i+2] = 255
    }
  }

  ctx.putImageData(imageData, 0, 0)

  // Timestamp overlay
  ctx.font = "14px monospace"
  ctx.fillStyle = "#fff"
  ctx.fillText("PLAY ▶ 00:03:42", 20, height - 20)
}
```

### 3. Pixel Art Rendering
```tsx
// Force nearest-neighbor scaling for crisp pixels
export function PixelArt({ src, scale = 4, className }: { src: string; scale?: number; className?: string }) {
  return (
    <img
      src={src}
      className={cn("block", className)}
      style={{
        imageRendering: "pixelated",
        width: `${scale * 100}%`,
        height: "auto",
      }}
    />
  )
}

// CSS pixel art with box-shadow (no image needed):
// Each "pixel" is a box-shadow offset
// Generate from grid: shadows.push(`${x}px ${y}px 0 ${color}`)
// Apply to 1x1px element with no background
```

### 4. Dithering Effect
```tsx
// Floyd-Steinberg dithering on canvas ImageData
const dither = (ctx: CanvasRenderingContext2D, w: number, h: number, palette: number[][]) => {
  const img = ctx.getImageData(0, 0, w, h)
  const d = img.data
  for (let y = 0; y < h; y++) {
    for (let x = 0; x < w; x++) {
      const i = (y * w + x) * 4
      const old = [d[i], d[i+1], d[i+2]]
      const nearest = findNearest(old, palette) // closest palette color
      d[i] = nearest[0]; d[i+1] = nearest[1]; d[i+2] = nearest[2]
      const err = [old[0]-nearest[0], old[1]-nearest[1], old[2]-nearest[2]]
      // Distribute error to neighbors (Floyd-Steinberg weights: 7/16, 3/16, 5/16, 1/16)
      distributeError(d, w, x+1, y, err, 7/16)
      distributeError(d, w, x-1, y+1, err, 3/16)
      distributeError(d, w, x, y+1, err, 5/16)
      distributeError(d, w, x+1, y+1, err, 1/16)
    }
  }
  ctx.putImageData(img, 0, 0)
}

// Ordered (Bayer) dithering — faster, grid pattern
// Use 4x4 or 8x8 threshold matrix
```

### 5. Retro Color Palettes
```tsx
const PALETTES = {
  gameboy: ["#0f380f", "#306230", "#8bac0f", "#9bbc0f"],
  nes: ["#000000", "#fcfcfc", "#f83800", "#7c7c7c", "#0058f8", "#00a800", "#fca044", "#d800cc"],
  c64: ["#000000", "#ffffff", "#880000", "#aaffee", "#cc44cc", "#00cc55", "#0000aa", "#eeee77",
        "#dd8855", "#664400", "#ff7777", "#333333", "#777777", "#aaff66", "#0088ff", "#bbbbbb"],
  cga: ["#000000", "#0000aa", "#00aa00", "#00aaaa", "#aa0000", "#aa00aa", "#aa5500", "#aaaaaa",
        "#555555", "#5555ff", "#55ff55", "#55ffff", "#ff5555", "#ff55ff", "#ffff55", "#ffffff"],
  vapor: ["#ff71ce", "#01cdfe", "#05ffa1", "#b967ff", "#fffb96"],
}
```

### 6. Glitch Effect
```tsx
// RGB channel split
export function GlitchText({ children, className }: { children: string; className?: string }) {
  return (
    <span className={cn("relative inline-block", className)} data-text={children}>
      <span className="absolute left-[2px] top-0 text-[#ff0000] mix-blend-multiply animate-[glitch-1_2s_infinite]">{children}</span>
      <span className="absolute left-[-2px] top-0 text-[#00ffff] mix-blend-multiply animate-[glitch-2_2s_infinite]">{children}</span>
      {children}
    </span>
  )
}
// Keyframes: random clip-path rectangles that shift position
// "glitch-1": { "0%,100%": { clipPath: "inset(20% 0 60% 0)" }, "25%": { clipPath: "inset(60% 0 10% 0)" }, ... }
```

### 7. ASCII Art Renderer
```tsx
const ASCII_CHARS = " .:-=+*#%@"
export function AsciiRenderer({ src, cols = 80 }: { src: string; cols?: number }) {
  // 1. Draw image to hidden canvas at low resolution
  // 2. Read pixel brightness at each cell
  // 3. Map brightness to ASCII character
  // 4. Render in <pre> with monospace font
  const charIndex = Math.floor((brightness / 255) * (ASCII_CHARS.length - 1))
  // Optional: color each character with original pixel color for color ASCII art
}
```

### 8. LED / Dot Matrix Display
```tsx
// Grid of circles that light up to form text/patterns
// Each "LED" has on/off/dim state
// Use 5x7 bitmap font for characters
// Animate scrolling text like a real marquee
// Colors: classic red, amber, or green on dark background
```

### 9. Demoscene Effects

#### Plasma
```tsx
// Classic color cycling plasma
for (let y = 0; y < h; y++) {
  for (let x = 0; x < w; x++) {
    const v = Math.sin(x * 0.05 + time)
      + Math.sin(y * 0.05 + time * 0.5)
      + Math.sin((x + y) * 0.03 + time * 0.3)
      + Math.sin(Math.sqrt(x*x + y*y) * 0.04)
    const color = hslToRgb((v + 4) / 8, 1, 0.5)
    // Set pixel
  }
}
```

#### Fire Effect
```tsx
// Bottom row: random white/yellow pixels
// Each frame: pixel = average of 3 below neighbors - decay
// Palette: black → dark red → red → orange → yellow → white
// Classic demoscene fire from the 90s
```

#### Tunnel / Rotozoom
```tsx
// For each pixel: convert to polar coordinates
// angle = atan2(y - cy, x - cx) + time
// distance = 1.0 / sqrt((x-cx)² + (y-cy)²)
// Use angle and distance as texture UV coordinates
// Animated time creates flying-through-tunnel effect
```

### 10. Retro UI Components
```tsx
// Window chrome (Windows 95 style)
export function RetroWindow({ title, children, className }: { title: string; children: React.ReactNode; className?: string }) {
  return (
    <div className={cn("border-2 border-[#dfdfdf] border-r-[#808080] border-b-[#808080] bg-[#c0c0c0]", className)}>
      <div className="flex items-center gap-1 bg-gradient-to-r from-[#000080] to-[#1084d0] px-2 py-1 text-sm font-bold text-white">
        {title}
        <div className="ml-auto flex gap-0.5">
          <button className="h-4 w-4 border border-[#dfdfdf] border-r-[#808080] border-b-[#808080] bg-[#c0c0c0] text-[10px] leading-none">_</button>
          <button className="h-4 w-4 border border-[#dfdfdf] border-r-[#808080] border-b-[#808080] bg-[#c0c0c0] text-[10px] leading-none">□</button>
          <button className="h-4 w-4 border border-[#dfdfdf] border-r-[#808080] border-b-[#808080] bg-[#c0c0c0] text-[10px] leading-none">×</button>
        </div>
      </div>
      <div className="p-2">{children}</div>
    </div>
  )
}
```

### 11. Pixelation Shader
```glsl
// Post-process any content to look pixelated
uniform float uPixelSize;
void main() {
  vec2 uv = gl_FragCoord.xy / uResolution;
  vec2 pixelUV = floor(uv * uResolution / uPixelSize) * uPixelSize / uResolution;
  gl_FragColor = texture2D(uTexture, pixelUV);
}
```

## Performance Rules

1. Use `image-rendering: pixelated` for CSS-based pixel scaling (GPU accelerated)
2. Pre-render static effects to a cached canvas, animate only what changes
3. Dithering is expensive — do it once on load, not per frame
4. CRT scanlines via CSS `repeating-linear-gradient` (zero JS cost)
5. Limit canvas resolution for retro effects (that's the point!)
6. Use `createImageBitmap` for async image processing

## Dependencies

| Package | Purpose | Install |
|---------|---------|---------|
| (none required) | Most effects are Canvas2D + CSS | — |
| three | Shader-based post-processing | `npm i three @types/three` |

**Version:** 1.0.0
**Last Updated:** 2026-03-17

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.