agentsclimarketplace

Html to images skill

Skill Alienxcript/html-export-skills/html-to-images skill

Export a single-file HTML slide deck as individual PNG images — one per slide. Renders in headless Chrome with fullPage screenshot, then slices with Pillow. Use when the user wants slide images for social media, docs, or any use case that doesn't need PPTX.From its SKILL.md

Install
npx -y skills add Alienxcript/html-export-skills --skill html-to-images skill

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

5.0 KB, ~1.3k tokens by cl100k_base, as published. Nobody here has run it

HTML to Images Skill

Export any single-file HTML slide deck as individual PNG images. Each slide becomes its own file: slide-01.png, slide-02.png, etc.

How It Works

  1. Headless Chrome renders the full HTML page at once (fullPage: true) → one tall PNG
  2. Pillow slices it into N equal strips and saves each as a numbered PNG

No PPTX, no python-pptx. Just clean image files ready for anywhere.


Requirements

ToolInstall
Node.jshttps://nodejs.org
Python 3https://python.org
Puppeteernpm install puppeteer (auto-handled)
Pillowpip install pillow
Chrome or EdgeAlready installed on most systems

Step-by-Step Instructions

1. Ask the user for inputs

You need:

  • HTML file path — the source presentation
  • Number of slides — how many slides the deck has
  • Output folder — where to save the images (default: a slides/ subfolder next to the HTML)
  • Output format — PNG (default) or JPEG
  • Resolution — default 1920×1080. Ask only if they want something different (e.g. 2560×1440 for retina, 1080×1080 for square social).

Auto-detect Chrome from these paths (check in order):

  • C:/Program Files/Google/Chrome/Application/chrome.exe
  • C:/Program Files (x86)/Google/Chrome/Application/chrome.exe
  • C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe
  • /usr/bin/google-chrome
  • /usr/bin/chromium-browser

2. Install puppeteer if needed

node -e "require('puppeteer')" 2>&1
npm install puppeteer --save-dev

3. Write the screenshot script

Write _screenshot.mjs to the same folder as the HTML:

import puppeteer from 'puppeteer';
import path from 'path';
import { fileURLToPath } from 'url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const HTML_PATH = path.join(__dirname, '<HTML_FILENAME>');
const W = <WIDTH>;   // e.g. 1920
const H = <HEIGHT>;  // e.g. 1080

const browser = await puppeteer.launch({
  executablePath: '<CHROME_PATH>',
  headless: true,
  args: ['--no-sandbox', `--window-size=${W},${H}`],
});

const page = await browser.newPage();
await page.setViewport({ width: W, height: H, deviceScaleFactor: 1 });
await page.goto(`file:///${HTML_PATH.replace(/\\/g, '/')}`, {
  waitUntil: 'networkidle0',
  timeout: 30000,
});

// Wait for fonts and transitions to settle
await new Promise(r => setTimeout(r, 2500));

// Freeze animations, hide fixed UI chrome
await page.addStyleTag({ content: `
  * { animation: none !important; transition: none !important; }
  body::after { display: none !important; }
  #nav, #progress, nav, header[data-fixed] { display: none !important; }
` });

await page.screenshot({
  path: path.join(__dirname, '_fullpage.png'),
  fullPage: true,
});

console.log('Full page captured.');
await browser.close();

4. Write the slice script

Write _slice_images.py to the same folder:

from PIL import Image
import os

FOLDER   = r'<FOLDER_PATH>'
FULLPAGE = os.path.join(FOLDER, '_fullpage.png')
OUT_DIR  = os.path.join(FOLDER, '<OUTPUT_SUBFOLDER>')  # e.g. 'slides'
SLIDES   = <SLIDE_COUNT>
FORMAT   = '<FORMAT>'  # 'PNG' or 'JPEG'
EXT      = 'jpg' if FORMAT == 'JPEG' else 'png'

os.makedirs(OUT_DIR, exist_ok=True)

img = Image.open(FULLPAGE)
W, H = img.size
slide_h = H // SLIDES

print(f"Full image: {W}x{H}, {SLIDES} slides, {slide_h}px each")
print(f"Saving to: {OUT_DIR}")

for i in range(SLIDES):
    top  = i * slide_h
    crop = img.crop((0, top, W, top + slide_h))
    out_path = os.path.join(OUT_DIR, f'slide-{i+1:02d}.{EXT}')
    if FORMAT == 'JPEG':
        crop = crop.convert('RGB')  # JPEG does not support transparency
        crop.save(out_path, format=FORMAT, quality=95)
    else:
        crop.save(out_path, format=FORMAT)
    print(f"  Saved slide-{i+1:02d}.{EXT} (rows {top}-{top+slide_h})")

print(f"\nDone. {SLIDES} images saved to {OUT_DIR}/")

5. Run both scripts

node _screenshot.mjs
python _slice_images.py

6. Clean up temp files

After confirming the images look correct, delete:

  • _fullpage.png
  • _screenshot.mjs
  • _slice_images.py

The output images in the slides/ folder are kept.


Output

slides/
├── slide-01.png
├── slide-02.png
├── slide-03.png
...
└── slide-10.png

Common Issues

ProblemCauseFix
All images identicalUsing scroll + viewport clipAlways use fullPage: true + slice — never scroll
Slide count wrongSlide height inconsistency in HTMLEnsure every slide is exactly 100vh
JPEG has black backgroundTransparency not supported by JPEGSkill auto-converts to RGB before saving
Chrome not foundWrong exe pathCheck Edge as fallback

What ships with it: 1 file

3.0 KB alongside SKILL.md

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.