Free

Painted Light

Luminous link preview cards: a centred wordmark or headline on layered, flowing light painted in code from your colours. One PNG per row, free of banding.

Install

Install the "Painted Light" skill into ~/.claude/skills/painted-light-og.
Download each file and save it under that folder:
https://skilllift.pages.dev/packs/painted-light-og/SKILL.md → SKILL.md
https://skilllift.pages.dev/packs/painted-light-og/references/style.md → references/style.md
https://skilllift.pages.dev/packs/painted-light-og/assets/engine.html → assets/engine.html
https://skilllift.pages.dev/packs/painted-light-og/scripts/render.mjs → scripts/render.mjs
https://skilllift.pages.dev/packs/painted-light-og/scripts/check.mjs → scripts/check.mjs
Then read SKILL.md, tell me what it does, and remind me to start a new session.

Unpack it into ~/.claude/skills

Same skill, different brief

The left is the preview. The right was made by a separate agent from a different brief, using only the installed skill and without seeing the preview.

The preview
The preview
A new brief, made by a separate agent
A new brief, made by a separate agent

One test run on a fictional brief. Letterforms can differ on systems without the same fonts.

Makes luminous link preview images by script with no image model. A centred wordmark or headline sits on sweeping light that moves warm to cool, generated in code from a seed as layered flowing bands with soft grain and depth, varied per row from the user's two to four colours, and measured free of banding. Takes a name and a headline, or a CSV or JSON list, and writes one 1200x630 PNG per row plus a 1600x900 copy. Use when someone asks for Open Graph images, link previews or social share cards for a product and wants a vivid, luminous look, or asks for this style by name.

What it must pass before delivery

  • node scripts/check.mjs exits 0 on the final folder, with --approved pointing at the approved text.
  • I viewed the 600 wide sheet and at least one card at full size.
  • The light looks painted and layered, with no visible steps, and runs warm to cool on every card.
  • All text is the user's approved text, in one language per card.
  • Fictional, sample or test material carries a notice.
  • No real brand's look or logo was used.
  • Inferred lines and assumptions are listed in the hand-over.
Read the full SKILL.md
---
name: painted-light-og
description: Makes luminous link preview images by script with no image model. A centred wordmark or headline sits on sweeping light that moves warm to cool, generated in code from a seed as layered flowing bands with soft grain and depth, varied per row from the user's two to four colours, and measured free of banding. Takes a name and a headline, or a CSV or JSON list, and writes one 1200x630 PNG per row plus a 1600x900 copy. Use when someone asks for Open Graph images, link previews or social share cards for a product and wants a vivid, luminous look, or asks for this style by name.
version: 1.0.0
---

# Painted Light

The look lives in `assets/engine.html`. You supply text, colours and seeds. You never edit the engine: `scripts/check.mjs` fails any card made by an edited engine. Read `references/style.md` only if you want the exact tokens.

## Inputs to ask for

Ask once, in one message, for whatever the brief does not give.

| Field | Meaning | Limit | If the brief is thin |
|---|---|---|---|
| `name` required | Company or product name, in type only | 32 Latin or 16 Chinese characters | Use the name exactly as the user wrote it. Never invent one for a real card |
| `title` required unless `lead` is `wordmark` | The headline, centred. Use `|` for an exact line break; each marked part becomes one line. For a literal bar write `\|` | 72 Latin or 30 Chinese characters | Reuse the user's own sentence, trimmed. If nothing usable exists, set `lead` to `wordmark`, leave `title` empty and list that as inferred. Never write a claim they did not make |
| `lead` optional | `headline` sets the title large with the name small above it. `wordmark` sets the name large with the title small below it | One of those two words | Leave empty for `headline`. Use `wordmark` for a home page, a launch, or when there is no headline |
| `notice` optional | A small label on a dark plate: fictional, sample, draft, test | 44 Latin or 22 Chinese characters, one line, same language as the title | Leave empty for a real, approved card. Required if the company is invented: `Fictional sample`. If the headline is not approved yet: `Draft, not approved` |
| `seed` optional | Fixes the light and grain. Same seed, same inputs, same pixels | 40 characters | Leave empty: the file name is the seed, so every row differs. Change it to get a different painting |
| `colors` optional | Two to four `#RRGGBB`, warm end first, cool end last. A list given cool first is reversed, with a warning. At full strength the light keeps these hues within 12 degrees, and the check enforces it. Blends between them are short and travel round the hue wheel, so amber to violet passes through red and magenta, never through brown | 2 to 4 values, no more. The two ends must differ in warmth, or the row is refused | Leave empty for the default and list that as an assumption. Do not guess a brand's colours |
| `file` optional | Output file name. Unsafe characters are cleaned, duplicates get a number | 60 characters | Leave empty: built from name and title |

Limits do not count spaces.

Colour rules the check enforces, in OKLCH on the pixels. Hue drift of any supplied colour at most 12 degrees. Each colour covers at least 24% of the bright light divided by the number of colours. One brown or grey region at most 6% of the card, and all of them at most 12%. If a row fails, change its `seed`: 24 of 28 seeds passed in testing. If that does not clear it, add a middle colour.

Lists are CSV with a header row, or JSON as a list of objects. Quotes, doubled quotes, line breaks inside quotes, a byte order mark and empty rows are handled. This pack takes no photo.

## Refuse these

- Logos of others are not accepted, and neither is a logo in place of the name. The engine rejects a `logo` field.
- If a request names a real company or brand and asks for its look, refuse that part and offer this pack's own look with the user's own name and colours. No list of names can be complete, so the script cannot catch this: you must judge. The same applies to a thinly described copy of a recognisable brand, and to cards that pretend to come from someone else.
- Do not name or imitate a real company in sample text. Use an invented name and a `notice`.

## Steps

1. Install once, if `playwright` is missing: `npm install playwright` then `npx playwright install chromium`. If it lives elsewhere, set `NODE_PATH` to that `node_modules`.
2. Write the list file with the fields above. Use only text the user approved.
3. Try one row at small size first, and render one at a time:
   `node scripts/render.mjs --in list.csv --out cards --row 1 --draft`
4. View that draft. Adjust `title` breaks, `colors` or `seed`. Repeat until it is right.
5. Render everything: `node scripts/render.mjs --in list.csv --out cards`. A single card also works: `--name "..." --title "..." --notice "..." --colors "#FF9A3C,#3A5BFF" --out cards`. A failing row is reported and the others still render; the exit code is non-zero.
6. Check, with your approved text as the reference:
   `node scripts/check.mjs cards --approved list.csv --sheet cards-600.png`
   Fix everything reported and run it again. Add `--require-notice` when the cards are fictional or sample. The check runs from this folder on cards with any file name, because each PNG carries its own layout record. PNG optimisers strip that record, so check before optimising.
7. View `cards-600.png`, which shows every card at 600 wide, and open at least one card at full size. The check cannot judge taste. If the light looks dull or the check reports muddy light, change the seed. If a line reads badly, set `|` breaks.

Render time is about 2 seconds per row on a laptop, both formats included, and the check takes under a second per card. Use `nice -n 10` on a shared machine. Run one render at a time and wait for it in the foreground. Never start detached processes, never run machine-wide kill commands, and run no image model.

## When something is missing

- Thin brief: restate the user's facts, invent nothing, and list every line you inferred. No customers, numbers, awards, dates or prices unless the user gave them.
- Nobody can answer: proceed with the rules in the table, and list your assumptions in the hand-over.
- A different length: the text falls through size steps by itself. If it stops fitting, the error says so. Shorten it.
- No real link: these are images, so no link is needed.
- A word stranded or a bad break: use `|`. Do not edit the PNG, since the check fails edited pixels.
- Very pale colours: the light dims behind the words until they measure readable. If it still reports low contrast, choose deeper colours.

## Deliverables

- One `name.png` at 1200x630 and one `name-16x9.png` at 1600x900 for each row, plus `manifest.json` listing files, size steps and lines.

## Before delivering

- [ ] `node scripts/check.mjs` exits 0 on the final folder, with `--approved` pointing at the approved text.
- [ ] I viewed the 600 wide sheet and at least one card at full size.
- [ ] The light looks painted and layered, with no visible steps, and runs warm to cool on every card.
- [ ] All text is the user's approved text, in one language per card.
- [ ] Fictional, sample or test material carries a `notice`.
- [ ] No real brand's look or logo was used.
- [ ] Inferred lines and assumptions are listed in the hand-over.

## When not to use it

- The user needs a photo, a screenshot or a quiet card.
- The colours must stay within one narrow hue, since this look needs a warm end and a cool end.
- The user wants a logo on the card, or a real brand's look.

## Requirements

Node.js 22 or later and Playwright with Chromium. The check needs only Node. No network is used, and the renderer blocks all web requests. Fonts are system stacks; see `references/style.md` for fallbacks. On a machine with no Chinese sans the renderer stops and says what to install.

Used in these workflows

Similar skills