<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Pixel → Lua Generator</title>
<style>
input[type=range] {
-webkit-appearance: none;
appearance: none;
width: 100%;
margin-top: 0.5rem;
}
input[type=range]:focus { outline: none; }
body {
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
min-height: 100vh;
background: #1e1e1e;
color: #cfcfcf;
font-family: Arial, sans-serif;
}
header, footer {
text-align: center;
padding: 1rem;
background: #2d2d2d;
}
main {
flex: 1;
padding: 1rem;
max-width: 700px;
margin: 0 auto;
}
label { display: block; margin-top: 1rem; }
input, button, textarea {
background: #2d2d2d;
border: 1px solid #3c3c3c;
color: #cfcfcf;
font-size: 1rem;
box-sizing: border-box;
}
input, textarea {
width: 100%;
padding: 0.5rem;
margin-top: 0.5rem;
}
button {
padding: 0.5rem;
margin-top: 0.5rem;
cursor: pointer;
}
.url-loader { display: flex; gap: 0.5rem; margin-top: 0.5rem; }
.url-loader input[type=text] { flex: 1; }
.url-loader button { flex: 0 0 80px; }
#original { display: block; margin: 1rem auto; max-width: 100%; height: auto; border: 1px solid #3c3c3c; }
#resolution, #outputResolution { text-align: center; margin-top: 0.25rem; color: #aaa; }
.slider-container { margin-top: 1rem; display: none; }
.slider-container span { font-weight: bold; }
.canvas-container { position: relative; display: inline-block; margin: 1rem auto; border: 1px solid #3c3c3c; }
#preview { display: block; max-width: 100%; height: auto; }
#luaNameContainer { display: none; margin-top: 1rem; }
#luaNameContainer .url-loader button { flex: 0 0 60px; }
#colorPicker { width: 100%; height: 2.5rem; border: none; margin-top: 1rem; cursor: pointer; }
#luaContainer { margin-top: 1rem; display: none; }
textarea { height: 200px; font-family: monospace; font-size: 0.9rem; resize: vertical; line-height: 1.2; }
#downloadBtn { width: 100%; }
footer { font-size: 0.9rem; }
</style>
</head>
<body>
<header><h1>Pixel → Lua Generator</h1></header>
<main>
<!-- File upload -->
<label for="fileInput">Choose pixel image:</label>
<input type="file" id="fileInput" accept="image/*" />
<!-- URL loader -->
<label for="imageUrl">Or paste image URL:</label>
<div class="url-loader">
<input type="text" id="imageUrl" placeholder="https://example.com/image.jpg" />
<button id="loadFromUrl">Load</button>
</div>
<!-- Lua name input (URL mode) -->
<div id="luaNameContainer">
<label for="luaName">Lua Filename (without extension):</label>
<div class="url-loader">
<input type="text" id="luaName" placeholder="custom_name" />
<button id="setNameBtn">Set</button>
</div>
</div>
<!-- Original preview -->
<img id="original" src="" alt="Original Upload" style="display:none;" />
<div id="resolution"></div>
<!-- Pixel size slider -->
<div class="slider-container" id="sliderContainer">
<label for="pixelRange">Pixel Size: <span id="pixelSizeLabel">1</span></label>
<input type="range" id="pixelRange" min="1" max="50" value="1" step="1" />
</div>
<!-- Canvas preview -->
<div class="canvas-container" id="canvasContainer" style="display:none;">
<canvas id="preview" width="0" height="0"></canvas>
</div>
<div id="outputResolution"></div>
<!-- Color picker -->
<label for="colorPicker">Pick Ignore Color:</label>
<input type="color" id="colorPicker" value="#000000" />
<!-- Lua output -->
<div id="luaContainer">
<label for="luaCode">Generated Lua</label>
<textarea id="luaCode" readonly></textarea>
</div>
<button id="downloadBtn" type="button" disabled>Export as .lua</button>
</main>
<footer>Made by Amar</footer>
<script>
const fileInput = document.getElementById('fileInput');
const imageUrl = document.getElementById('imageUrl');
const loadFromUrl = document.getElementById('loadFromUrl');
const originalImg = document.getElementById('original');
const resolutionDiv = document.getElementById('resolution');
const outputResDiv = document.getElementById('outputResolution');
const pixelRange = document.getElementById('pixelRange');
const pixelSizeLabel = document.getElementById('pixelSizeLabel');
const sliderContainer = document.getElementById('sliderContainer');
const canvasContainer = document.getElementById('canvasContainer');
const preview = document.getElementById('preview');
const ctx = preview.getContext('2d');
const luaNameContainer = document.getElementById('luaNameContainer');
const luaNameInput = document.getElementById('luaName');
const setNameBtn = document.getElementById('setNameBtn');
const colorPicker = document.getElementById('colorPicker');
const luaContainer = document.getElementById('luaContainer');
const luaCode = document.getElementById('luaCode');
const downloadBtn = document.getElementById('downloadBtn');
let img = new Image();
let imgName = 'image';
let origW = 0, origH = 0;
let loaded = false;
let ignoreHex = '000000';
let sourceUrl = '';
function onImageLoaded() {
origW = img.width; origH = img.height; loaded = true;
sliderContainer.style.display = 'block';
canvasContainer.style.display = 'inline-block';
luaContainer.style.display = 'block';
downloadBtn.disabled = false;
originalImg.src = img.src; originalImg.style.display = 'block';
resolutionDiv.textContent = `${origW}×${origH}`;
luaNameContainer.style.display = sourceUrl ? 'block' : 'none';
drawPixelated(); updateLuaPreview();
}
fileInput.addEventListener('change', e => {
const file = e.target.files[0]; if (!file) return;
imgName = file.name.replace(/\.[^.]+$/, ''); sourceUrl = '';
img = new Image(); img.onload = onImageLoaded; img.src = URL.createObjectURL(file);
});
loadFromUrl.addEventListener('click', () => {
const url = imageUrl.value.trim(); if (!url) return alert('Enter valid URL');
imgName = url.split('/').pop().split('.')[0] || 'image'; sourceUrl = url;
img = new Image(); img.crossOrigin = 'anonymous';
img.onload = onImageLoaded; img.onerror = () => alert('Load failed.');
img.src = url;
});
pixelRange.addEventListener('input', () => {
pixelSizeLabel.textContent = pixelRange.value;
if (!loaded) return;
drawPixelated();
updateLuaPreview();
});
setNameBtn.addEventListener('click', () => {
if (luaNameInput.value.trim()) imgName = luaNameInput.value.trim();
updateLuaPreview();
});
colorPicker.addEventListener('input', () => {
ignoreHex = colorPicker.value.slice(1);
if (loaded) updateLuaPreview();
});
function drawPixelated() {
const size = +pixelRange.value;
const tmpW = Math.ceil(origW / size);
const tmpH = Math.ceil(origH / size);
const tmp = document.createElement('canvas');
tmp.width = tmpW;
tmp.height = tmpH;
const tctx = tmp.getContext('2d'); tctx.imageSmoothingEnabled = false;
tctx.drawImage(img, 0, 0, tmpW, tmpH);
preview.width = origW;
preview.height = origH;
ctx.imageSmoothingEnabled = false;
ctx.clearRect(0, 0, origW, origH);
ctx.drawImage(tmp, 0, 0, tmpW, tmpH, 0, 0, origW, origH);
// update output resolution
outputResDiv.textContent = `${tmpW}×${tmpH}`;
}
function rgbToHex(r, g, b) {
return ((1 << 24) | (r << 16) | (g << 8) | b).toString(16).slice(1);
}
function buildLuaString() {
const size = +pixelRange.value;
const tmpW = Math.ceil(origW / size);
const tmpH = Math.ceil(origH / size);
const tmp = document.createElement('canvas'); tmp.width = tmpW; tmp.height = tmpH;
const tctx = tmp.getContext('2d'); tctx.imageSmoothingEnabled = false;
tctx.drawImage(img, 0, 0, tmpW, tmpH);
const data = tctx.getImageData(0, 0, tmpW, tmpH).data;
const rows = [];
for (let y = 0; y < tmpH; y++) {
const cols = [];
for (let x = 0; x < tmpW; x++) {
const i = (y * tmpW + x) * 4;
const alpha = data[i + 3];
const hex = alpha === 0 ? 'ffffff' : rgbToHex(data[i], data[i + 1], data[i + 2]);
cols.push(`"${hex}"`);
}
rows.push(' { ' + cols.join(', ') + ' },');
}
return [
'--[[',
`Title: ${imgName}`,
`Platform: ${sourceUrl ? new URL(sourceUrl).hostname : 'local'}`,
`url: ${sourceUrl}`,
']]--',
'return {',
` title = "${imgName}",`,
` name = "${imgName}",`,
` width = ${tmpW},`,
` height= ${tmpH},`,
` ignore_colors = [ "${ignoreHex}" ],`,
' frames = {',
' {',
...rows,
' }',
' }',
'}'
].join('\n');
}
function updateLuaPreview() {
luaCode.value = buildLuaString();
}
downloadBtn.addEventListener('click', () => {
const blob = new Blob([luaCode.value], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${imgName}.lua`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
</script>
</body>
</html>