{"id":746,"date":"2025-03-16T02:16:52","date_gmt":"2025-03-16T02:16:52","guid":{"rendered":"https:\/\/divbydev.com\/?page_id=746"},"modified":"2025-03-21T18:45:38","modified_gmt":"2025-03-21T18:45:38","slug":"js-pixel-art-tool","status":"publish","type":"page","link":"https:\/\/divbydev.com\/index.php\/projects-2\/js-pixel-art-tool\/","title":{"rendered":"JS Pixel Art Tool"},"content":{"rendered":"\n<p>Stuff<\/p>\n\n\n\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <title>JS Pixel Art Tool<\/title>\n  <style>\n    #canvasContainer {\n      position: relative;\n      display: inline-block;\n    }\n    #baseCanvas, #gridCanvas {\n      border: 1px solid #ccc;\n      display: block;\n    }\n    #gridCanvas {\n      position: absolute;\n      top: 0;\n      left: 0;\n      pointer-events: none;\n    }\n    #palette {\n      margin: 10px 0;\n    }\n    .palette-swatch {\n      width: 30px;\n      height: 30px;\n      display: inline-block;\n      margin-right: 5px;\n      border: 2px solid #fff;\n      cursor: pointer;\n    }\n    .palette-swatch.selected {\n      border: 2px solid #000;\n    }\n  <\/style>\n<\/head>\n<body>\n  <h2>Pixel Art Converter &#038; Editor<\/h2>\n  <input type=\"file\" id=\"fileInput\" accept=\"image\/*\">\n  <br>\n  <button id=\"pixelateBtn\">Pixelate Image<\/button>\n  <button id=\"undoBtn\">Undo<\/button>\n  <button id=\"saveBtn\">Save Image<\/button>\n  <button id=\"clearBtn\">Clear Canvas<\/button>\n  <br><br>\n  <button id=\"decreaseSizeBtn\">Decrease Block Size<\/button>\n  <button id=\"increaseSizeBtn\">Increase Block Size<\/button>\n  <span id=\"cellSizeDisplay\">Block Size: 16px<\/span>\n  <br><br>\n  <h3>Palette &#038; Color Picker<\/h3>\n  <input type=\"color\" id=\"colorPicker\" value=\"#ff0000\">\n  <div id=\"palette\"><\/div>\n  <input type=\"text\" id=\"newColorInput\" placeholder=\"Enter hex color (e.g., #ff00ff)\">\n  <button id=\"addColorBtn\">Add Color<\/button>\n  <br><br>\n  <p>Click on a cell to fill it with the selected color.<\/p>\n  <div id=\"canvasContainer\">\n    <canvas id=\"baseCanvas\" width=\"400\" height=\"400\"><\/canvas>\n    <canvas id=\"gridCanvas\" width=\"400\" height=\"400\"><\/canvas>\n  <\/div>\n  \n  <script>\n    (function() {\n      let defaultCellSize = 16;\n      let cellSize = defaultCellSize;\n      let img = new Image();\n\n      const fileInput = document.getElementById(\"fileInput\");\n      const pixelateBtn = document.getElementById(\"pixelateBtn\");\n      const undoBtn = document.getElementById(\"undoBtn\");\n      const saveBtn = document.getElementById(\"saveBtn\");\n      const clearBtn = document.getElementById(\"clearBtn\");\n      const decreaseSizeBtn = document.getElementById(\"decreaseSizeBtn\");\n      const increaseSizeBtn = document.getElementById(\"increaseSizeBtn\");\n      const cellSizeDisplay = document.getElementById(\"cellSizeDisplay\");\n\n      const colorPicker = document.getElementById(\"colorPicker\");\n      const paletteDiv = document.getElementById(\"palette\");\n      const newColorInput = document.getElementById(\"newColorInput\");\n      const addColorBtn = document.getElementById(\"addColorBtn\");\n\n      const baseCanvas = document.getElementById(\"baseCanvas\");\n      const baseCtx = baseCanvas.getContext(\"2d\");\n      const gridCanvas = document.getElementById(\"gridCanvas\");\n      const gridCtx = gridCanvas.getContext(\"2d\");\n\n      let paletteColors = [\"#000000\", \"#ffffff\", \"#ff0000\", \"#00ff00\", \"#0000ff\", \"#ffff00\", \"#ff00ff\", \"#00ffff\"];\n      let currentColor = paletteColors[0];\n      let undoStack = [];\n\n      \/\/ Initialize canvas with a white background\n      baseCtx.fillStyle = \"#ffffff\";\n      baseCtx.fillRect(0, 0, baseCanvas.width, baseCanvas.height);\n      drawGrid();\n\n      function renderPalette() {\n        paletteDiv.innerHTML = \"\";\n        paletteColors.forEach((color) => {\n          const swatch = document.createElement(\"div\");\n          swatch.classList.add(\"palette-swatch\");\n          swatch.style.backgroundColor = color;\n          if (color.toLowerCase() === currentColor.toLowerCase()) swatch.classList.add(\"selected\");\n          swatch.addEventListener(\"click\", () => {\n            currentColor = color;\n            colorPicker.value = color;\n            renderPalette();\n          });\n          paletteDiv.appendChild(swatch);\n        });\n      }\n      renderPalette();\n\n      function updateCellSizeDisplay() {\n        cellSizeDisplay.textContent = `Block Size: ${cellSize}px`;\n      }\n      updateCellSizeDisplay();\n\n      function pushState() {\n        undoStack.push(baseCanvas.toDataURL());\n        if (undoStack.length > 20) undoStack.shift();\n      }\n\n      undoBtn.addEventListener(\"click\", () => {\n        if (undoStack.length === 0) {\n          alert(\"Nothing to undo.\");\n          return;\n        }\n        const lastState = undoStack.pop();\n        const imgState = new Image();\n        imgState.onload = () => {\n          baseCtx.clearRect(0, 0, baseCanvas.width, baseCanvas.height);\n          baseCtx.drawImage(imgState, 0, 0);\n          drawGrid();\n        };\n        imgState.src = lastState;\n      });\n\n      saveBtn.addEventListener(\"click\", () => {\n        const dataURL = baseCanvas.toDataURL(\"image\/png\");\n        const link = document.createElement(\"a\");\n        link.href = dataURL;\n        link.download = \"pixel_art.png\";\n        link.click();\n      });\n\n      clearBtn.addEventListener(\"click\", () => {\n        baseCtx.fillStyle = \"#ffffff\";\n        baseCtx.fillRect(0, 0, baseCanvas.width, baseCanvas.height);\n        undoStack = [];\n        cellSize = defaultCellSize;\n        updateCellSizeDisplay();\n        drawGrid();\n      });\n\n      decreaseSizeBtn.addEventListener(\"click\", () => {\n        if (cellSize > 2) {\n          cellSize -= 2;\n          updateCellSizeDisplay();\n          drawGrid();\n        }\n      });\n      increaseSizeBtn.addEventListener(\"click\", () => {\n        cellSize += 2;\n        updateCellSizeDisplay();\n        drawGrid();\n      });\n\n      colorPicker.addEventListener(\"input\", () => {\n        currentColor = colorPicker.value;\n        renderPalette();\n      });\n\n      fileInput.addEventListener(\"change\", (e) => {\n        const file = e.target.files[0];\n        if (!file) return;\n        const reader = new FileReader();\n        reader.onload = function(event) {\n          img = new Image();\n          img.onload = () => {\n            baseCanvas.width = img.width;\n            baseCanvas.height = img.height;\n            gridCanvas.width = img.width;\n            gridCanvas.height = img.height;\n            baseCtx.drawImage(img, 0, 0);\n            drawGrid();\n            pushState();\n          };\n          img.onerror = () => alert(\"Error loading image.\");\n          img.src = event.target.result;\n        };\n        reader.readAsDataURL(file);\n      });\n\n      pixelateBtn.addEventListener(\"click\", () => {\n        if (!img.src || img.src === \"\") return alert(\"Please load an image first!\");\n        pushState();\n        const width = baseCanvas.width;\n        const height = baseCanvas.height;\n        const imageData = baseCtx.getImageData(0, 0, width, height);\n        const data = imageData.data;\n\n        for (let y = 0; y < height; y += cellSize) {\n          for (let x = 0; x < width; x += cellSize) {\n            let r = 0, g = 0, b = 0, count = 0;\n            const blockWidth = Math.min(cellSize, width - x);\n            const blockHeight = Math.min(cellSize, height - y);\n            for (let j = 0; j < blockHeight; j++) {\n              for (let i = 0; i < blockWidth; i++) {\n                const posX = x + i;\n                const posY = y + j;\n                const index = (posY * width + posX) * 4;\n                r += data[index];\n                g += data[index + 1];\n                b += data[index + 2];\n                count++;\n              }\n            }\n            r = Math.round(r \/ count);\n            g = Math.round(g \/ count);\n            b = Math.round(b \/ count);\n            baseCtx.fillStyle = `rgb(${r}, ${g}, ${b})`;\n            baseCtx.fillRect(x, y, blockWidth, blockHeight);\n          }\n        }\n        drawGrid();\n      });\n\n      baseCanvas.addEventListener(\"click\", (e) => {\n        const rect = baseCanvas.getBoundingClientRect();\n        const clickX = e.clientX - rect.left;\n        const clickY = e.clientY - rect.top;\n        const cellX = Math.floor(clickX \/ cellSize) * cellSize;\n        const cellY = Math.floor(clickY \/ cellSize) * cellSize;\n        pushState();\n        baseCtx.fillStyle = currentColor;\n        baseCtx.fillRect(cellX, cellY, cellSize, cellSize);\n        drawGrid();\n      });\n\n      function drawGrid() {\n        gridCtx.clearRect(0, 0, gridCanvas.width, gridCanvas.height);\n        gridCtx.strokeStyle = \"rgba(0,0,0,0.3)\";\n        gridCtx.lineWidth = 1;\n        for (let x = 0; x < gridCanvas.width; x += cellSize) {\n          gridCtx.beginPath();\n          gridCtx.moveTo(x + 0.5, 0);\n          gridCtx.lineTo(x + 0.5, gridCanvas.height);\n          gridCtx.stroke();\n        }\n        for (let y = 0; y < gridCanvas.height; y += cellSize) {\n          gridCtx.beginPath();\n          gridCtx.moveTo(0, y + 0.5);\n          gridCtx.lineTo(gridCanvas.width, y + 0.5);\n          gridCtx.stroke();\n        }\n      }\n\n      addColorBtn.addEventListener(\"click\", () => {\n        const newColor = newColorInput.value.trim();\n        if (\/^#[0-9A-Fa-f]{6}$\/.test(newColor)) {\n          if (!paletteColors.includes(newColor)) {\n            paletteColors.push(newColor);\n            renderPalette();\n            newColorInput.value = \"\";\n          }\n        } else {\n          alert(\"Please enter a valid hex color (e.g., #ff00ff).\");\n        }\n      });\n    })();\n  <\/script>\n<\/body>\n<\/html>\n\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\n","protected":false},"excerpt":{"rendered":"<p>Stuff JS Pixel Art Tool Pixel Art Converter &#038; Editor Pixelate Image Undo Save Image Clear Canvas Decrease Block Size Increase Block Size Block Size: 16px Palette &#038; Color Picker Add Color Click on a cell to fill it with the selected color. \u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":230,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-746","page","type-page","status-publish"],"_links":{"self":[{"href":"https:\/\/divbydev.com\/index.php\/wp-json\/wp\/v2\/pages\/746","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/divbydev.com\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/divbydev.com\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/divbydev.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/divbydev.com\/index.php\/wp-json\/wp\/v2\/comments?post=746"}],"version-history":[{"count":5,"href":"https:\/\/divbydev.com\/index.php\/wp-json\/wp\/v2\/pages\/746\/revisions"}],"predecessor-version":[{"id":767,"href":"https:\/\/divbydev.com\/index.php\/wp-json\/wp\/v2\/pages\/746\/revisions\/767"}],"up":[{"embeddable":true,"href":"https:\/\/divbydev.com\/index.php\/wp-json\/wp\/v2\/pages\/230"}],"wp:attachment":[{"href":"https:\/\/divbydev.com\/index.php\/wp-json\/wp\/v2\/media?parent=746"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}