Skip to main content
skill CX Codex

ray-so-code-snippet — Claude Code skill: code images via ray.so

This skill turns a code snippet into a clean image through ray.so and saves it to your working directory. Handy when you need to show code in a chat or a post.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add intellectronica/agent-skills --skill ray-so-code-snippet -a claude-code
Open on GitHub

What ray-so-code-snippet does

The skill takes your code, builds a ray.so URL with all the styling baked in, and captures the result with agent-browser. Theme, padding, dark mode, background, line numbers and title are all asked up front, and the list of available themes and languages is pulled from the ray.so repository with curl. Language detection often happens on its own from the file extension or the syntax, so you may not be asked at all.

One hard limit: no agent-browser in PATH, no image. The skill checks for it first and stops there. You also need python3, which handles the base64 and URL encoding. Every ray.so parameter goes into the URL hash after #, not the query string — put them in the wrong place and the styling is ignored. Leave width out unless you need a fixed frame; ray.so sizes the frame to the code on its own.

The image lands in the current working directory under a sensible name like rayso-snippet.png, or after the title if you gave one. A different path only if you asked for it.

What it helps with

  • turn a code snippet into an image for a post
  • capture a syntax-highlighted screenshot of code
  • pick a theme and padding for a code image
  • save the finished image into the project folder
  • share code in chat without losing formatting

Example prompts for the agent

  • make a ray.so image of this code, midnight theme
  • generate a snippet image from fibonacci.py with a title
  • screenshot this code with line numbers and save it to the current folder

How to install ray-so-code-snippet

1Skills CLI (Vercel)

npx skills add intellectronica/agent-skills --skill ray-so-code-snippet -a claude-code

2Manually: put the files in place

~/.claude/skills/ray-so-code-snippet/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/ray-so-code-snippet/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about ray-so-code-snippet

How do I install ray-so-code-snippet in Claude?

Run npx skills add intellectronica/agent-skills --skill ray-so-code-snippet -a claude-code. The skill ends up at ~/.claude/skills/ray-so-code-snippet/SKILL.md.

What does ray-so-code-snippet do?

It builds a ray.so URL with your code and chosen styling, opens it in a browser, and saves the capture locally. Theme, padding, background and line numbers are set before generation.

What does the skill need to run?

agent-browser in PATH — without it the skill stops at the first step. python3 handles the base64 and URL encoding.

Source text

SKILL.md · CC0-1.0
---
name: ray-so-code-snippet
description: Generate beautiful code snippet images using ray.so. This skill should be used when the user asks to create a code image, code screenshot, code snippet image, or wants to make their code look pretty for sharing. Saves images locally to the current working directory or a user-specified path.
---

# ray.so Code Snippet Image Generator

Generate beautiful code snippet images using [ray.so](https://ray.so/) and save them locally.

## Requirements

- The user MUST provide the code snippet, either directly or by pointing to a file/selection in context
- MUST ask the user for ALL styling parameters before generating, presenting ALL available options
- MUST use `agent-browser` for screenshot capture (check availability first)

## Workflow

### Step 1: Verify agent-browser Availability

Before proceeding, verify that `agent-browser` is available:

```bash
which agent-browser
```

If `agent-browser` is not found in the PATH, inform the user that this skill requires agent-browser and cannot proceed without it.

### Step 2: Fetch Available Options

Fetch the current themes and languages from ray.so's GitHub repository using curl:

```bash
# Fetch and parse available themes
curl -s "https://raw.githubusercontent.com/raycast/ray-so/main/app/(navigation)/(code)/store/themes.ts" | grep -oE 'id:\s*"[^"]+"' | sed 's/id:\s*"//;s/"//' | sort -u

# Fetch and parse available languages
curl -s "https://raw.githubusercontent.com/raycast/ray-so/main/app/(navigation)/(code)/util/languages.ts" | grep -oE '^[[:space:]]*"?[a-zA-Z0-9+#-]+"?\s*:\s*\{' | sed 's/[[:space:]]*"//g;s/".*//;s/:.*//' | sort -u
```

### Step 3: Ask User for ALL Parameters

MUST use AskUserQuestion to ask for EVERY parameter, presenting ALL available options. Ask for parameters in this order:

#### 3.1 Theme Selection

Present ALL available themes. In the question, list every theme fetched from step 2. Example:

```
Question: "Which theme would you like?"
Description: "Available themes: [list ALL themes from curl output]"
Options (pick 4 popular ones for quick select):
- breeze (default, purple gradient)
- midnight (cyan-blue)
- vercel (minimalist dark)
- sunset (warm orange)
Note: User can select "Other" to type any theme from the full list
```

#### 3.2 Language Selection

**Infer the language when possible.** Skip this question if:
- The user explicitly specified a language
- The code comes from a file with a clear extension (e.g., `.py` → python, `.js` → javascript, `.ts` → typescript, `.rs` → rust, `.go` → go, etc.)
- The syntax is unmistakably identifiable (e.g., `def`/`import` → python, `func`/`package` → go, `fn`/`let mut` → rust)

Only ask this question if the language cannot be confidently inferred:

```
Question: "Which language for syntax highlighting?"
Description: "Available languages: [list ALL languages from curl output]"
Options:
- auto (auto-detect)
- javascript
- python
- typescript
Note: User can select "Other" to type any language from the full list
```

#### 3.3 Dark/Light Mode

```
Question: "Dark or light mode?"
Options:
- Dark mode (default)
- Light mode
```

#### 3.4 Background

```
Question: "Show the gradient background?"
Options:
- Yes, show background (default)
- No, transparent/minimal background
```

#### 3.5 Padding

```
Question: "How much padding around the code?"
Options:
- 16 (compact)
- 32 (small)
- 64 (medium, default)
- 128 (large)
```

#### 3.6 Line Numbers

```
Question: "Show line numbers?"
Options:
- No (default)
- Yes
```

#### 3.7 Title

```
Question: "Add a title above the code? (e.g., filename)"
Options:
- No title (default)
- Yes, add title
If yes, ask for the title text.
```

Note: Do NOT ask about output path/filename. Save to the current working directory with a sensible filename (e.g., `rayso-snippet.png`, or based on the title if provided like `fibonacci.png`). Only use a different path if the user explicitly specifies one in their original request.

### Step 4: Build the ray.so URL

**CRITICAL: ALL parameters must be in the URL hash (after #), NOT in the query string.**

Build the URL using shell commands:

```bash
# 1. Base64 encode the code
CODE_BASE64=$(echo -n 'YOUR_CODE_HERE' | base64)

# 2. URL encode the base64 string
CODE_ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$CODE_BASE64'))")

# 3. Build the URL with ALL parameters in the hash
# Format: https://ray.so/#param1=value1&param2=value2&code=ENCODED_CODE
# Do NOT include width parameter - let ray.so auto-size to fit content
URL="https://ray.so/#theme=THEME&padding=PADDING&background=BACKGROUND&darkMode=DARKMODE&language=LANGUAGE&code=${CODE_ENCODED}"

# Add optional parameters if needed:
# If lineNumbers: add "&lineNumbers=true" before &code=
# If title: add "&title=URL_ENCODED_TITLE" before &code=
```

**URL Hash Parameters:**
| Parameter | Values | Default |
|-----------|--------|---------|
| theme | Any theme from list | breeze |
| padding | 16, 32, 64, 128 | 64 |
| background | true, false | true |
| darkMode | true, false | true |
| language | Any language from list, or "auto" | auto |
| lineNumbers | true, false | false |
| title | URL-encoded string | (none) |
| width | Number (pixels) | auto |
| code | Base64-encoded, then URL-encoded | (required) |

**Note on width:** Do NOT include the `width` parameter unless you specifically need a fixed width. Without it, ray.so auto-sizes the frame to fit the code content, avoiding unnecessary empty space.

**Example URL construction:**
```bash
# For code: for i in range(23):\n    print(i)
# Theme: midnight, Padding: 64, Dark mode: true, Background: true, Language: python, Title: test.py

CODE='for i in range(23):
    print(i)'
CODE_BASE64=$(echo -n "$CODE" | base64)
CODE_ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$CODE_BASE64'))")
TITLE_ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('test.py'))")
URL="https://ray.so/#theme=midnight&padding=64&background=true&darkMode=true&language=python&title=${TITLE_ENCODED}&code=${CODE_ENCODED}"
echo "$URL"
```

### Step 5: Capture High-Quality Image with agent-browser

MUST use agent-browser (verified in Step 1). This approach uses the `html-to-image` library (same as ray.so's internal export) with high pixelRatio for crisp, sharp text rendering.

**IMPORTANT:** Always use a unique session name with `--session` to avoid stale session issues.

```bash
# Generate unique session name
SESSION="rayso-$(date +%s)"

# 1. Set viewport
agent-browser --session $SESSION set viewport 1400 900

# 2. Open the URL
agent-browser --session $SESSION open "$URL"

# 3. Wait for the page to fully render
agent-browser --session $SESSION wait --load networkidle
agent-browser --session $SESSION wait 3000

# 4. Load html-to-image library (same library ray.so uses internally)
agent-browser --session $SESSION eval 'new Promise((r,e)=>{const s=document.createElement("script");s.src="https://cdn.jsdelivr.net/npm/[email protected]/dist/html-to-image.js";s.onload=r;s.onerror=e;document.head.appendChild(s)})'

# 5. Capture at 4x resolution using html-to-image (produces crisp text)
agent-browser --session $SESSION eval 'htmlToImage.toPng(document.querySelector("#frame > div"),{pixelRatio:4,skipAutoScale:true})' > /tmp/rayso-dataurl-$SESSION.txt

# 6. Close the browser
agent-browser --session $SESSION close

# 7. Convert data URL to PNG file
DATAURL=$(cat /tmp/rayso-dataurl-$SESSION.txt | tr -d '"' | tr -d '\n')
echo "$DATAURL" | sed 's/data:image\/png;base64,//' | base64 -d > /path/to/output.png

# 8. Clean up temp file
rm /tmp/rayso-dataurl-$SESSION.txt
```

**Critical notes:**
- Uses `html-to-image` library which is what ray.so uses for its own export feature
- `pixelRatio: 4` produces high-DPI images with crisp, sharp text (4x native resolution)
- The data URL is captured directly from the library, not from a screenshot
- No ImageMagick required - pure browser-based rendering at high resolution
- Output is correctly sized with no extra whitespace

### Step 6: Confirm Output and STOP

Report the saved file location to the user. **The task is complete - do not perform any additional checks, explorations, or verifications after the screenshot is saved.**

## Complete Example

User: "Create a code snippet image of this Python function"

```python
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)
```

1. Check `which agent-browser` - confirmed available

2. Fetch themes and languages:
```bash
curl -s "https://raw.githubusercontent.com/raycast/ray-so/main/app/(navigation)/(code)/store/themes.ts" | grep -oE 'id:\s*"[^"]+"' | sed 's/id:\s*"//;s/"//' | sort -u
```

3. Ask user for parameters via AskUserQuestion:
   - Theme: user selects "midnight"
   - Language: *inferred as python from `def` syntax - not asked*
   - Dark mode: user selects "Dark mode"
   - Background: user selects "Yes"
   - Padding: user selects "64"
   - Line numbers: user selects "No"
   - Title: user selects "No title"

4. Build URL (all params in hash, no width for auto-sizing):
```bash
CODE='def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)'
CODE_BASE64=$(echo -n "$CODE" | base64)
CODE_ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$CODE_BASE64'))")
URL="https://ray.so/#theme=midnight&padding=64&background=true&darkMode=true&language=python&code=${CODE_ENCODED}"
```

5. Capture high-quality image:
```bash
SESSION="rayso-$(date +%s)"

agent-browser --session $SESSION set viewport 1400 900
agent-browser --session $SESSION open "$URL"
agent-browser --session $SESSION wait --load networkidle
agent-browser --session $SESSION wait 3000

# Load html-to-image library
agent-browser --session $SESSION eval 'new Promise((r,e)=>{const s=document.createElement("script");s.src="https://cdn.jsdelivr.net/npm/[email protected]/dist/html-to-image.js";s.onload=r;s.onerror=e;document.head.appendChild(s)})'

# Capture at 4x resolution
agent-browser --session $SESSION eval 'htmlToImage.toPng(document.querySelector("#frame > div"),{pixelRatio:4,skipAutoScale:true})' > /tmp/rayso-dataurl-$SESSION.txt
agent-browser --session $SESSION close

# Save as PNG
DATAURL=$(cat /tmp/rayso-dataurl-$SESSION.txt | tr -d '"' | tr -d '\n')
echo "$DATAURL" | sed 's/data:image\/png;base64,//' | base64 -d > ./fibonacci.png
rm /tmp/rayso-dataurl-$SESSION.txt
```

6. Report: "Saved code snippet image to ./fibonacci.png"

## Image Resolution and Quality

This skill uses the `html-to-image` library with `pixelRatio: 4` to produce high-quality images with crisp, sharp text. This is the same rendering approach that ray.so uses for its built-in export feature.

**Output quality:**
- Default: 4x native resolution (frame auto-sizes to content, then rendered at 4x)
- Text is rendered at high DPI, not upscaled from low resolution
- Gradient backgrounds and all CSS styling are preserved
- No unnecessary empty space (frame auto-sizes to fit code)

**Adjusting resolution:**
- For smaller files: Change `pixelRatio:4` to `pixelRatio:2` in the eval command
- For maximum quality: Use `pixelRatio:6` (same as ray.so's "6x" export option)

**Forcing a specific width:**
- Only add `&width=NUMBER` to the URL if you need a fixed width (e.g., for consistent sizing across multiple images)

## Troubleshooting

- **If agent-browser is not available:** Inform the user and do not proceed
- If curl fails to fetch themes/languages, use these common defaults:
  - Themes: breeze, midnight, candy, crimson, falcon, meadow, raindrop, sunset, vercel, supabase, tailwind
  - Languages: auto, javascript, typescript, python, rust, go, java, ruby, swift, kotlin, css, html, json, yaml, bash
- **If parameters aren't applied:** Ensure ALL parameters are in the URL hash (after #), not the query string
- **If title isn't showing:** The title parameter must be in the hash: `#title=filename.py&code=...`
- **If html-to-image fails to load:** Check network connectivity; the library loads from jsdelivr CDN
- **If capture returns empty:** The frame selector `#frame > div` may have changed; inspect the page structure
- For very long code snippets, ray.so may truncate; consider splitting into multiple images
- If the page doesn't load properly, increase the wait time (try 4000ms or more)
- **If you get a blank page:** Use a fresh unique session name with `--session` flag
- **If data URL is malformed:** Ensure quotes and newlines are stripped before base64 decoding

© intellectronica · CC0-1.0 · intellectronica/agent-skills

Similar skills

All skills for Agent setup: memory, context, orchestration →

From the same set20

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor