Skip to content

Instantly share code, notes, and snippets.

@AaronKow
Created July 2, 2025 02:30
Show Gist options
  • Save AaronKow/596543f2f3cba515e503faf7b32409cf to your computer and use it in GitHub Desktop.
Save AaronKow/596543f2f3cba515e503faf7b32409cf to your computer and use it in GitHub Desktop.
ASCII → RP2040 Sprite Converter
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>ASCII → RP2040 Sprite Converter</title>
<style>
body { font-family: system-ui, sans-serif; margin: 1rem; background: #f4f4f5; }
h1 { font-size: 1.5rem; margin-bottom: 1rem; }
textarea, pre { width: 100%; box-sizing: border-box; border: 1px solid #ddd; border-radius: 0.5rem; padding: 0.75rem; font-family: monospace; background: #fff; }
textarea { min-height: 160px; resize: vertical; }
button {
padding: 0.6rem 1rem; margin-top: 0.75rem; border: none;
border-radius: 0.5rem; cursor: pointer; font-weight: 600;
background: #2563eb; color: #fff;
}
button:disabled { opacity: 0.5; cursor: not-allowed; }
.flex { display: flex; gap: 1rem; flex-wrap: wrap; }
.half { flex: 1 1 300px; min-width: 300px; }
label { font-weight: 600; display: block; margin-bottom: 0.25rem; }
</style>
</head>
<body>
<h1>ASCII → C Sprite Converter (SSD1306 128×64)</h1>
<button id="load-demo">Load Demo</button>
<div class="flex">
<div class="half">
<label for="ascii">1️⃣ Draw sprite (non-space & non-dot = pixel ON)</label>
<textarea id="ascii" placeholder="Example (16×16 smiley using . for blank):&#10................&#10.##############.&#10.#....##....##..&#10.#....##....##..&#10.#............#.&#10.#....######..#.&#10.#............#.&#10.#..##....##..#.&#10.#...######...#.&#10.##############.&#10................&#10................&#10................&#10................&#10................&#10................"></textarea>
</div>
<div class="half">
<label for="c-output">2️⃣ Generated C code</label>
<textarea id="c-output" readonly placeholder="C array will appear here…"></textarea>
<button id="copy" disabled>Copy&nbsp;to&nbsp;Clipboard</button>
</div>
</div>
<button id="convert">Convert → C Bitmap</button>
<pre id="info"></pre>
<script>
const asciiEl = document.getElementById('ascii');
const outputEl = document.getElementById('c-output');
const infoEl = document.getElementById('info');
const convertBtn = document.getElementById('convert');
const copyBtn = document.getElementById('copy');
const loadDemoBtn = document.getElementById('load-demo');
const demoSprite = [
"................",
".##############.",
".#....##....##..",
".#....##....##..",
".#............#.",
".#....######..#.",
".#............#.",
".#..##....##..#.",
".#...######...#.",
".##############.",
"................",
"................",
"................",
"................",
"................",
"................"
].join("\n");
function asciiToBytes(lines) {
const height = lines.length;
const width = Math.max(...lines.map(l => l.length));
if (width > 128 || height > 64) throw new Error('Sprite exceeds 128×64 limit');
// Pad lines to equal width
lines = lines.map(l => l.padEnd(width, ' '));
const bytes = [];
for (let y = 0; y < height; y += 8) {
for (let x = 0; x < width; x++) {
let byte = 0;
for (let bit = 0; bit < 8; bit++) {
const yy = y + bit;
let pxOn = false;
if (yy < height) {
const ch = lines[yy][x];
pxOn = ch !== ' ' && ch !== '.'; // spaces & dots are OFF
}
byte |= (pxOn ? 1 : 0) << bit; // LSB = top pixel
}
bytes.push(byte);
}
}
return { width, height, bytes };
}
function generateC({ width, height, bytes }) {
const hex = bytes.map(b => '0x' + b.toString(16).padStart(2, '0'));
return `// Width: ${width}, Height: ${height}\nconst uint8_t sprite_${width}x${height}[${bytes.length}] = {\n ${hex.join(', ')}\n};`;
}
convertBtn.addEventListener('click', () => {
try {
const lines = asciiEl.value.replace(/\r/g, '').split('\n').filter(l => l.length);
if (!lines.length) throw new Error('Please enter ASCII art');
const data = asciiToBytes(lines);
const cCode = generateC(data);
outputEl.value = cCode;
infoEl.textContent = `Converted: ${data.width}×${data.height} → ${data.bytes.length} bytes`;
copyBtn.disabled = false;
} catch (err) {
infoEl.textContent = err.message;
outputEl.value = '';
copyBtn.disabled = true;
}
});
copyBtn.addEventListener('click', async () => {
try {
await navigator.clipboard.writeText(outputEl.value);
copyBtn.textContent = 'Copied!';
setTimeout(() => (copyBtn.textContent = 'Copy to Clipboard'), 1500);
} catch {
alert('Clipboard failed. Please copy manually.');
}
});
loadDemoBtn.addEventListener('click', () => {
asciiEl.value = demoSprite;
outputEl.value = '';
infoEl.textContent = '';
copyBtn.disabled = true;
});
</script>
<details style="margin-top:1rem;">
<summary><strong>How to use in C (TinyUSB / Pico‑SDK)</strong></summary>
<pre>
// Include the array this tool generated (e.g. sprite_16x16)
#include "sprite.h"
void oled_draw_sprite(uint8_t x, uint8_t page, const uint8_t *sprite,
uint8_t w, uint8_t h) {
/* SSD1306 page‑addressed write */
for (uint8_t col = 0; col < w; col++) {
for (uint8_t p = 0; p < (h + 7) / 8; p++) {
ssd1306_set_cursor(x + col, page + p);
ssd1306_write_data(sprite[col + p * w]);
}
}
}
// Example usage
oled_draw_sprite(0, 0, sprite_16x16, 16, 16);
</pre>
</details>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment