Create R-based pictogram glyphs for skill, agent, or team icons in the visualization layer. Covers concept sketching, ggplot2 layer composition using the primitives library, color strategy, registration in the appropriate glyph mapping file and manifest, rendering via the build pipeline, and visual verification of the neon-glow output. Use when a new entity has been added and needs a visual icon for the force-graph visualization, an existing glyph needs replacement, or when batch-creating gly...
Scanned 9/3/2026
Install to Claude Code
npx -y skills add pjt222/agent-almanac --skill create-glyph --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Create Glyph?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/pjt222-create-glyph-226c4122)More formats (shields.io, HTML) on the badges page.
---
name: create-glyph
locale: caveman
source_locale: en
source_commit: 82c77053
fence_basis_commit: 82c77053
translator: "Julius Brussee homage — caveman"
translation_date: "2026-04-19"
description: >
Create R-based pictogram glyphs for skill, agent, or team icons in the
visualization layer. Covers concept sketching, ggplot2 layer composition using
the primitives library, color strategy, registration in the appropriate glyph
mapping file and manifest, rendering via the build pipeline, and visual
verification of the neon-glow output. Use when a new entity has been added and
needs a visual icon for the force-graph visualization, an existing glyph needs
replacement, or when batch-creating glyphs for a new domain.
license: MIT
allowed-tools: Read Write Edit Bash Grep Glob
metadata:
author: Philipp Thoss
version: "2.0"
domain: design
complexity: intermediate
language: R
tags: design, glyph, pictogram, icon, ggplot2, visualization, neon
---
# Create Glyph
Make R-based pictogram glyphs for skill, agent, or team icons in `viz/` visualization layer. Each glyph is pure-ggplot2 function. Draws recognizable shape on 100x100 canvas. Rendered with neon glow to transparent-background WebP.
## When Use
- New skill, agent, or team added. Needs visual icon
- Existing glyph needs replacement or redesign
- Batch-creating glyphs for new skills domain
- Prototyping visual metaphors for entity concepts
## Inputs
- **Required**: Entity type — `skill`, `agent`, or `team`
- **Required**: Entity ID (e.g., `create-glyph`, `mystic`, `r-package-review`) and domain (for skills)
- **Required**: Visual concept — what glyph should depict
- **Optional**: Reference glyph to study for complexity level
- **Optional**: Custom `--glow-sigma` value (default: 4)
## Steps
### Step 1: Concept — Design the Visual Metaphor
Identify entity being iconified. Pick visual metaphor.
1. Read entity's source file. Understand core concept:
- Skills: `skills/<id>/SKILL.md`
- Agents: `agents/<id>.md`
- Teams: `teams/<id>.md`
2. Pick metaphor type:
- **Literal object**: flask for experiments, shield for security
- **Abstract symbol**: arrows for merging, spirals for iteration
- **Composite**: combine 2-3 simple shapes (e.g., document + pen)
3. Reference existing glyphs for complexity calibration:
```text
Complexity Tiers:
+----------+--------+-------------------------------------------+
| Tier | Layers | Examples |
+----------+--------+-------------------------------------------+
| Simple | 2 | glyph_flame, glyph_heartbeat |
| Moderate | 3-5 | glyph_document, glyph_experiment_flask |
| Complex | 6+ | glyph_ship_wheel, glyph_bridge_cpp |
+----------+--------+-------------------------------------------+
```
4. Pick function name: `glyph_<descriptive_name>` (snake_case, unique)
**Got:** Clear mental sketch of shape with 2-6 planned layers.
**If fail:** Concept too abstract? Fall back to related concrete object. Review existing glyphs in same domain for inspiration.
### Step 2: Compose — Write the Glyph Function
Write R function producing ggplot2 layers.
1. Function signature (immutable contract):
```r
glyph_<name> <- function(cx, cy, s, col, bright) {
# cx, cy = center coordinates (50, 50 on 100x100 canvas)
# s = scale factor (1.0 = fill ~70% of canvas)
# col = domain color hex (e.g., "#ff88dd" for design)
# bright = brightened variant of col (auto-computed by renderer)
# Returns: list() of ggplot2 layers
}
```
2. Apply scale factor `* s` to ALL dimensions for consistent scaling:
```r
r <- 20 * s # radius
hw <- 15 * s # half-width
lw <- .lw(s) # line width (default base 2.5)
lw_thin <- .lw(s, 1.2) # thinner line width
```
3. Build geometry using available primitives:
| Geometry | Usage |
|---|---|
| `ggplot2::geom_polygon(data, .aes(x, y), ...)` | Filled shapes |
| `ggplot2::geom_path(data, .aes(x, y), ...)` | Open lines/curves |
| `ggplot2::geom_segment(data, .aes(x, xend, y, yend), ...)` | Line segments, arrows |
| `ggplot2::geom_rect(data, .aes(xmin, xmax, ymin, ymax), ...)` | Rectangles |
| `ggforce::geom_circle(data, .aes(x0, y0, r), ...)` | Circles |
4. Apply color strategy:
```text
Alpha Guide:
+----------------------+------------+--------------------------+
| Purpose | Alpha | Example |
+----------------------+------------+--------------------------+
| Large fill (body) | 0.08-0.15 | hex_with_alpha(col, 0.1) |
| Medium fill (accent) | 0.15-0.25 | hex_with_alpha(col, 0.2) |
| Small fill (detail) | 0.25-0.35 | hex_with_alpha(bright, 0.3) |
| Outline stroke | 1.0 | color = bright |
| Secondary stroke | 1.0 | color = col |
| No fill | --- | fill = NA |
+----------------------+------------+--------------------------+
```
5. Return flat `list()` of layers. Renderer iterates and wraps each with glow.
6. Place function in right primitives file by entity type:
- **Skills**: domain-grouped across 19 primitives files:
- `primitives.R` — bushcraft, compliance, containerization, data-serialization, defensive
- `primitives_2.R` — devops, general, git, mcp-integration
- `primitives_3.R` — mlops, observability, PM, r-packages, reporting, review, web-dev, esoteric, design
- More `primitives_4.R` through `primitives_19.R` for newer domains
- **Agents**: `viz/R/agent_primitives.R`
- **Teams**: `viz/R/team_primitives.R`
**Got:** Working R function returning list of 2-6 ggplot2 layers.
**If fail:** `ggforce::geom_circle` errors? Confirm ggforce installed. Coords off? Canvas is 100x100 with (0,0) at bottom-left. Test function interactively:
```r
source("viz/R/utils.R"); source("viz/R/primitives.R") # etc.
layers <- glyph_<name>(50, 50, 1.0, "#ff88dd", "#ffa8f0")
p <- ggplot2::ggplot() + ggplot2::coord_fixed(xlim=c(0,100), ylim=c(0,100)) +
ggplot2::theme_void()
for (l in layers) p <- p + l
print(p)
```
### Step 3: Register — Map Entity to Glyph
Add entity-to-glyph mapping in right glyph mapping file.
**For skills:**
1. Open `viz/R/glyphs.R`
2. Find comment section for target domain (e.g., `# -- design (3)`)
3. Add entry in alphabetical order within domain block:
```r
"skill-id" = "glyph_function_name",
```
4. Update domain count in comment if applicable
**For agents:**
1. Open `viz/R/agent_glyphs.R`
2. Find alphabetical position in `AGENT_GLYPHS`
3. Add entry:
```r
"agent-id" = "glyph_function_name",
```
**For teams:**
1. Open `viz/R/team_glyphs.R`
2. Find alphabetical position in `TEAM_GLYPHS`
3. Add entry:
```r
"team-id" = "glyph_function_name",
```
5. Verify no duplicate ID exists in target list
**Got:** Right `*_GLYPHS` list has new mapping.
**If fail:** Build later reports "No glyph mapped"? Double-check entity ID exactly matches one in manifest and registry.
### Step 4: Manifest — Add Icon Entry
Register icon in right manifest file.
**For skills:** `viz/data/icon-manifest.json`
```json
{
"skillId": "skill-id",
"domain": "domain-name",
"prompt": "<domain basePrompt>, <descriptors>, dark background, vector art",
"seed": <next_seed>,
"path": "public/icons/cyberpunk/<domain>/<skill-id>.webp",
"status": "pending"
}
```
**For agents:** `viz/data/agent-icon-manifest.json`
```json
{
"agentId": "agent-id",
"prompt": "<agent-specific descriptors>, dark background, vector art",
"seed": <next_seed>,
"path": "public/icons/cyberpunk/agents/<agent-id>.webp",
"status": "pending"
}
```
**For teams:** `viz/data/team-icon-manifest.json`
```json
{
"teamId": "team-id",
"prompt": "<team-specific descriptors>, dark background, vector art",
"seed": <next_seed>,
"path": "public/icons/cyberpunk/teams/<team-id>.webp",
"status": "pending"
}
```
**Got:** Valid JSON with new entry placed among type siblings.
**If fail:** Validate JSON syntax. Common mistakes: trailing comma after last array element, missing quotes.
### Step 5: Render — Generate the Icon
Run icon pipeline to render new glyph. Always use `build.sh` as entry point — it handles platform detection and R binary selection. See [render-icon-pipeline](../render-icon-pipeline/SKILL.md) for full flag reference and pipeline architecture.
```bash
# From project root — renders all palettes, standard + HD, skips existing icons
bash viz/build.sh --only <domain> --skip-existing # skills
bash viz/build.sh --type agent --only <id> --skip-existing # agents
bash viz/build.sh --type team --only <id> --skip-existing # teams
# Dry run first:
bash viz/build.sh --only <domain> --dry-run
```
`build.sh` runs full pipeline (palette → data → manifest → render → terminal glyphs). Non-render steps add ~10 seconds but keep all data current.
Output locations:
- Skills: `viz/public/icons/<palette>/<domain>/<skill-id>.webp`
- Agents: `viz/public/icons/<palette>/agents/<agent-id>.webp`
- Teams: `viz/public/icons/<palette>/teams/<team-id>.webp`
**Got:** Log shows `OK: <entity> (seed=XXXXX, XX.XKB)`. WebP file exists.
**If fail:**
- `"No glyph mapped"` — Step 3 mapping missing or typo
- `"Unknown domain"` — Domain not in `get_palette_colors()` in `palettes.R`
- R package errors — Run `install.packages(c("ggplot2", "ggforce", "ggfx", "ragg", "magick"))` first
- Rendering crashes? Test glyph function interactively (see Step 2 fallback)
### Step 6: Verify — Visual Inspection
Check rendered output meets quality standards.
1. Verify file exists and has reasonable size:
```bash
ls -la viz/public/icons/cyberpunk/<type-path>/<entity-id>.webp
# Expected: 15-80 KB typical range
```
2. Open WebP in image viewer. Check:
- Shape reads clearly at full size (1024x1024)
- Neon glow present but not overpowering
- Background transparent (no black/white rectangle)
- No clipping at canvas edges
3. Check at small sizes (icon renders at ~40-160px in force graph):
- Shape stays recognizable
- Detail does not turn to noise
- Glow does not overwhelm shape
**Got:** Clear, recognizable pictogram with even neon glow on transparent background.
**If fail:**
- Glow too strong: re-render with `--glow-sigma 2` (default is 4)
- Glow too weak: re-render with `--glow-sigma 8`
- Shape unreadable small? Simplify glyph (fewer layers, bolder strokes, bump `.lw(s, base)` base value)
- Clipping at edges? Shrink shape dimensions or shift center
### Step 7: Iterate — Refine if Needed
Adjust and re-render.
1. Common adjustments:
- **Bolder strokes**: bump `.lw(s, base)` — try `base = 3.0` or `3.5`
- **More visible fill**: bump alpha from 0.10 to 0.15-0.20
- **Shape proportions**: tune multipliers on `s` (e.g., `20 * s` -> `24 * s`)
- **Add/remove detail layers**: keep total layers 2-6 for best results
2. Re-render after changes:
```bash
# Delete the existing icon first, then re-render
rm viz/public/icons/cyberpunk/<type-path>/<entity-id>.webp
# Use the appropriate build command from Step 5
```
3. Satisfied? Verify manifest status shows `"done"`. Build script updates it on success.
**Got:** Final icon passes all verification checks from Step 6.
**If fail:** After 3+ iterations glyph still reads poorly? Consider completely different visual metaphor (back to Step 1).
## Reference
### Domain and Entity Color Palettes
All domain colors (for skills) in `viz/R/palettes.R` (single source of truth). Agent and team colors also in `palettes.R`. Cyberpunk palette (hand-tuned neon colors) in `get_cyberpunk_colors()`. Viridis-family palettes auto-generated via `viridisLite`.
Look up color:
```r
source("viz/R/palettes.R")
get_palette_colors("cyberpunk")$domains[["design"]] # skill domain
get_palette_colors("cyberpunk")$agents[["mystic"]] # agent
get_palette_colors("cyberpunk")$teams[["tending"]] # team
```
Adding new domain? Add to three places in `palettes.R`:
1. `PALETTE_DOMAIN_ORDER` (alphabetical)
2. `get_cyberpunk_colors()` domains list
3. Run `bash viz/build.sh` to regenerate palettes, data, manifests
### Glyph Function Catalog
See full catalog of available glyph functions in primitives source files:
- **Skills**: `viz/R/primitives.R` through `viz/R/primitives_19.R` (domain-grouped)
- **Agents**: `viz/R/agent_primitives.R`
- **Teams**: `viz/R/team_primitives.R`
### Helper Functions
| Function | Signature | Purpose |
|---|---|---|
| `.lw(s, base)` | `(scale, base=2.5)` | Scale-aware line width |
| `.aes(...)` | alias for `ggplot2::aes` | Shorthand aesthetic mapping |
| `hex_with_alpha(hex, alpha)` | `(string, 0-1)` | Add alpha to hex color |
| `brighten_hex(hex, factor)` | `(string, factor=1.3)` | Brighten a hex color |
| `dim_hex(hex, factor)` | `(string, factor=0.4)` | Dim a hex color |
## Checks
- [ ] Glyph function follows `glyph_<name>(cx, cy, s, col, bright) -> list()` signature
- [ ] All dimensions use `* s` scaling factor
- [ ] Color strategy uses `col` for fills, `bright` for outlines, `hex_with_alpha()` for transparency
- [ ] Function placed in right primitives file for entity type and domain
- [ ] Glyph mapping entry added in right `*_glyphs.R` file
- [ ] Manifest entry added with right entity ID, path, `"status": "pending"`
- [ ] Build command runs without error (dry-run first)
- [ ] Rendered WebP exists at expected path
- [ ] File size in expected range (15-80 KB)
- [ ] Icon reads clearly at both 1024px and ~40px display sizes
- [ ] Transparent background (no solid rectangle behind glyph)
- [ ] Manifest status updated to `"done"` after successful render
## Pitfalls
- **Forgetting `* s`**: Hard-coded pixel values break when scale changes. Always multiply by `s`.
- **Canvas origin confusion**: (0,0) is bottom-left, not top-left. Higher `y` values move UP.
- **Double glow**: Renderer already applies `ggfx::with_outer_glow()` to every layer. Do NOT add glow inside glyph function.
- **Too many layers**: Each layer gets individual glow wrapping. More than 8 layers → slow rendering, noisy visuals.
- **Mismatched IDs**: Entity ID in glyph mapping, manifest, registry must all match exactly.
- **JSON trailing commas**: Manifest is strict JSON. No trailing comma after last array element.
- **Missing domain color**: Domain not in `get_cyberpunk_colors()` in `palettes.R`? Rendering errors. Add color first, then regenerate.
- **Wrong primitives file**: Skills in domain-grouped `primitives*.R`, agents in `agent_primitives.R`, teams in `team_primitives.R`.
## See Also
- `enhance-glyph` — improve existing glyph's visual quality, fix rendering issues, add detail layers
- `audit-icon-pipeline` — detect missing glyphs and icons, know what needs creating
- `render-icon-pipeline` — run full rendering pipeline end-to-end
- `ornament-style-mono` — complementary AI-based image generation (Z-Image vs R-coded glyphs)
- `ornament-style-color` — color theory for glyph accent fill decisions
- `create-skill` — parent workflow triggering glyph creation when adding new skills
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!