| name | simon-html-tools |
| description | Build single-file HTML tools following Simon Willison's patterns - self-contained HTML+JS+CSS applications optimized for LLM generation, no build step, CDN dependencies. Use when creating browser-based tools, utilities, or demos that should be (1) Self-contained in one HTML file, (2) Easy to distribute and host statically, (3) Quick to prototype with LLMs, (4) Client-side only with no server requirements. Ideal for data visualization, API explorers, format converters, debugging tools, and interactive demos. |
Simon's HTML Tools
Build production-quality single-file HTML tools following patterns from Simon Willison's collection.
Core Philosophy
HTML tools are self-contained browser applications that:
- Combine HTML, CSS, and JavaScript in a single file
- Load dependencies from CDNs (no build step)
- Can be copy-pasted from LLM responses
- Host anywhere as static files
- Remain small and maintainable (typically <500 lines)
Essential Principles
1. Single File, No Build Step
Always avoid React and build tools:
Prompt: "Build a JSON to YAML converter. No React."
Why no React:
- JSX requires compilation
- Slower to render in LLM environments
- More prone to bugs
- Harder to copy/paste and distribute
2. Load Dependencies from CDNs
Use version-pinned URLs from cdnjs.com or jsdelivr.com:
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-yaml/4.1.0/js-yaml.min.js"></script>
3. Keep It Small
Target <500 lines total:
- Easy for LLMs to understand and modify
- Can be rewritten from scratch in minutes
- Maintainability becomes less critical
Implementation Patterns
State Persistence in URL
Store application state in the URL for bookmarking and sharing:
// Save state to hash
function saveState(state) {
window.location.hash = encodeURIComponent(JSON.stringify(state));
}
// Load state from hash
function loadState() {
if (!window.location.hash) return null;
try {
return JSON.parse(decodeURIComponent(window.location.hash.slice(1)));
} catch (e) {
return null;
}
}
Secrets in localStorage
Never put API keys in URLs or code. Use localStorage:
function getApiKey(serviceName) {
const key = localStorage.getItem(`${serviceName}_api_key`);
if (!key) {
const newKey = prompt(`Enter your ${serviceName} API key:`);
if (newKey) localStorage.setItem(`${serviceName}_api_key`, newKey);
return newKey;
}
return key;
}
Advanced Clipboard Operations
document.addEventListener('paste', async (e) => {
e.preventDefault();
for (const item of e.clipboardData.items) {
if (item.type.startsWith('image/')) {
const file = item.getAsFile();
// Handle image
} else if (item.type === 'text/html') {
const html = await new Promise(r => item.getAsString(r));
// Handle HTML
}
}
});
// Copy to clipboard with fallback
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
} catch (err) {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
}
}
Working with Local Files
document.getElementById('fileInput').addEventListener('change', async (e) => {
const file = e.target.files[0];
// Read as text
const text = await file.text();
// Or as Data URL
const reader = new FileReader();
reader.onload = (e) => processImage(e.target.result);
reader.readAsDataURL(file);
});
Generate Downloadable Files
function downloadText(content, filename, mimeType = 'text/plain') {
const blob = new Blob([content], { type: mimeType });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
CORS-Enabled APIs
// PyPI
async function fetchPackage(name) {
const res = await fetch(`https://pypi.org/pypi/${name}/json`);
return await res.json();
}
// GitHub raw files
async function fetchGitHubFile(owner, repo, path) {
const res = await fetch(
`https://raw.githubusercontent.com/${owner}/${repo}/main/${path}`
);
return await res.text();
}
LLM API Direct Access
async function callClaude(prompt) {
const apiKey = getApiKey('claude');
const res = await fetch('https://api.anthropic.com/v1/messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': apiKey,
'anthropic-version': '2023-06-01'
},
body: JSON.stringify({
model: 'claude-3-haiku-20240307',
max_tokens: 1024,
messages: [{ role: 'user', content: prompt }]
})
});
return await res.json();
}
Pyodide for Python
let pyodide = null;
async function initPyodide() {
if (!pyodide) {
pyodide = await loadPyodide();
await pyodide.loadPackage(['numpy', 'pandas']);
}
return pyodide;
}
async function runPython(code) {
const py = await initPyodide();
return await py.runPythonAsync(code);
}
Template Structure
See references/template.md for complete HTML template with best practices.
Useful Libraries from CDN
Data Processing:
- js-yaml, papaparse, jszip, dompurify
Visualization:
- Chart.js, Plotly, Leaflet, highlight.js
File Processing:
- PDF.js, Tesseract.js, exif-js, mammoth
Utilities:
- date-fns, fuse.js, marked, diff
Hosting
Recommended: GitHub Pages
- Create repository
- Settings → Pages → Deploy from branch
- Copy HTML file
- Access at
https://username.github.io/repo/tool.html
Avoid LLM platforms (restrictive sandboxes, warnings, less reliable)
Development Workflow
1. Prototype in Claude/ChatGPT
Build an artifact that converts CSV to JSON with preview.
Use PapaParse from CDN. Add download button. No React.
2. Upgrade to Coding Agent
For complex projects: Claude Code or Codex CLI
3. Remix Existing Tools
Look at the pypi-changelog tool. Build a similar tool
for npm packages showing version diffs.
Checklist
- Single HTML file (no build step)
- "No React" in prompts
- CDN dependencies with versions
- Code under 500 lines
- Error handling for APIs
- API keys in localStorage
- State in URL or localStorage
- Mobile responsive
- Footer with source link
Resources
- references/template.md - Complete HTML template structure
- references/patterns.md - Detailed implementation patterns
- references/libraries.md - Comprehensive CDN library catalog
- references/examples.md - Real-world tool examples