place pixels faster
// ==UserScript==
// @name Accelerator
// @namespace http://tampermonkey.net/
// @version 1.4.0
// @description place pixels faster
// @author zZedo
// @match https://pixelplace.io/*-*
// @license MIT
// @grant none
// @run-at document-start
// @require https://update.greasyfork.org/scripts/498080/1395134/Hacktimer.js
// ==/UserScript==
(function () {
'use strict';
const SPEEDS = {
slow: { ms: 12 },
medium: { ms: 9 },
fast: { ms: 5 },
};
const WS_INTERVAL = 1000; // ms between websocket packets
let enabled = true;
let spaceHeld = false;
let autoMode = false;
let currentSpeed = 'medium';
let dispatching = false;
let panelVisible = true;
let hookedSocket = null;
const OriginalWS = window.WebSocket;
window.WebSocket = function (url, protocols) {
const socket = protocols ? new OriginalWS(url, protocols) : new OriginalWS(url);
socket.addEventListener('open', () => {
if (url.includes('pixelplace.io')) {
hookedSocket = socket;
updateUI();
}
});
socket.addEventListener('close', () => {
if (hookedSocket === socket) hookedSocket = null;
updateUI();
});
return socket;
};
window.WebSocket.prototype = OriginalWS.prototype;
function getMouseData() {
const coords = document.getElementById('coordinates')?.textContent || '0,0';
const [x, y] = coords.split(',').map(c => parseInt(c.trim()));
const color = document.querySelector('#palette-buttons a.selected')?.getAttribute('data-id');
return { x, y, color: parseInt(color) };
}
const _channel = new MessageChannel();
let _channelActive = false;
let _channelCallback = null;
_channel.port2.onmessage = () => {
if (_channelActive && _channelCallback) {
_channelCallback();
_channel.port1.postMessage(null);
}
};
function startChannel(cb) {
_channelCallback = cb;
_channelActive = true;
_channel.port1.postMessage(null);
}
function stopChannel() {
_channelActive = false;
_channelCallback = null;
}
function kbTick() {
dispatching = true;
const canvas = document.querySelector('canvas');
const target = canvas || document.body;
const opts = {
bubbles: true, cancelable: true,
code: 'Space', key: ' ', keyCode: 32, which: 32, view: window,
};
target.dispatchEvent(new KeyboardEvent('keydown', opts));
target.dispatchEvent(new KeyboardEvent('keyup', opts));
dispatching = false;
}
function wsTick() {
if (!hookedSocket || hookedSocket.readyState !== 1) return;
const { x, y, color } = getMouseData();
if (!isNaN(x) && !isNaN(y) && !isNaN(color)) {
hookedSocket.send(`42["p",[${x},${y},${color},1]]`);
}
}
function makeLoop(tickFn, ms) {
let timer = null;
let expected = performance.now() + ms;
function step() {
if (!timer) return;
tickFn();
const drift = performance.now() - expected;
expected += ms;
timer = setTimeout(step, Math.max(0, ms - drift));
}
timer = setTimeout(step, ms);
return {
stop: () => { clearTimeout(timer); timer = null; },
get active() { return timer !== null; }
};
}
let kbRunning = false;
let wsLoop = null;
function startLoops() {
if (!kbRunning) {
kbRunning = true;
startChannel(kbTick);
}
if (!wsLoop && hookedSocket) wsLoop = makeLoop(wsTick, WS_INTERVAL);
}
function stopLoops() {
if (kbRunning) {
kbRunning = false;
stopChannel();
}
if (wsLoop) { wsLoop.stop(); wsLoop = null; }
}
function isPlacing() {
return enabled && (spaceHeld || autoMode);
}
function syncPlacing() {
isPlacing() ? startLoops() : stopLoops();
updateUI();
}
window.addEventListener('keydown', (e) => {
if (dispatching) return;
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
if (e.code === 'Space' && !e.repeat) {
spaceHeld = true;
if (enabled) startLoops();
updateUI();
}
}, true);
window.addEventListener('keyup', (e) => {
if (dispatching) return;
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
if (e.code === 'Space') {
spaceHeld = false;
if (!autoMode) stopLoops();
updateUI();
}
if (e.shiftKey && e.code === 'KeyK') {
if (!enabled) return;
autoMode = !autoMode;
syncPlacing();
}
if (e.shiftKey && e.code === 'KeyN') {
panelVisible = !panelVisible;
panel.style.display = panelVisible ? 'block' : 'none';
}
}, true);
const style = document.createElement('style');
style.textContent = `
@import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Orbitron:wght@700&display=swap');
#sca-panel {
position: fixed; bottom: 24px; right: 24px; z-index: 999999;
background: #0a0a0f; border: 1px solid #1e1e2e; border-radius: 12px;
padding: 16px 18px; width: 200px;
box-shadow: 0 0 30px rgba(0,0,0,0.7), 0 0 0 1px #1e1e2e;
font-family: 'Share Tech Mono', monospace; user-select: none; cursor: default;
}
#sca-panel .sca-header {
font-family: 'Orbitron', sans-serif; font-size: 11px; letter-spacing: 0.18em;
color: #555577; text-transform: uppercase; margin-bottom: 14px;
display: flex; align-items: center; gap: 8px;
}
#sca-panel .sca-dot {
width: 7px; height: 7px; border-radius: 50%; background: #333344;
flex-shrink: 0; transition: background 0.2s, box-shadow 0.2s;
}
#sca-panel .sca-dot.placing { background: #4ade80; box-shadow: 0 0 7px #4ade80; animation: sca-pulse 0.4s infinite alternate; }
#sca-panel .sca-dot.standby { background: #facc15; box-shadow: 0 0 5px #facc1588; }
@keyframes sca-pulse { from { opacity: 1; } to { opacity: 0.2; } }
#sca-panel .sca-status {
font-size: 9px; color: #333355; letter-spacing: 0.06em; margin-bottom: 10px;
padding: 4px 8px; border: 1px solid #111122; border-radius: 5px;
background: #0d0d18; text-align: center;
}
#sca-panel .sca-btn-row { display: flex; gap: 7px; margin-bottom: 12px; }
#sca-panel .sca-toggle, #sca-panel .sca-auto {
flex: 1; padding: 10px 0; border-radius: 8px; border: 1px solid #222233;
background: #111122; color: #666688; font-family: 'Share Tech Mono', monospace;
font-size: 11px; letter-spacing: 0.08em; cursor: pointer; transition: all 0.2s;
}
#sca-panel .sca-toggle:hover, #sca-panel .sca-auto:hover { border-color: #444466; color: #ccccee; }
#sca-panel .sca-toggle.on { background: #0d1f12; border-color: #4ade80; color: #4ade80; box-shadow: 0 0 10px rgba(74,222,128,0.15); }
#sca-panel .sca-auto.on { background: #1a0d1f; border-color: #c084fc; color: #c084fc; box-shadow: 0 0 10px rgba(192,132,252,0.2); animation: sca-glow 1.2s infinite alternate; }
@keyframes sca-glow { from { box-shadow: 0 0 6px rgba(192,132,252,0.15); } to { box-shadow: 0 0 18px rgba(192,132,252,0.45); } }
#sca-panel .sca-speeds { display: flex; flex-direction: column; gap: 6px; }
#sca-panel .sca-speed-btn {
width: 100%; padding: 8px 12px; border-radius: 6px; border: 1px solid #1e1e2e;
background: transparent; color: #44445a; font-family: 'Share Tech Mono', monospace;
font-size: 12px; letter-spacing: 0.12em; cursor: pointer;
display: flex; justify-content: space-between; align-items: center; transition: all 0.15s;
}
#sca-panel .sca-speed-btn:hover { border-color: #333355; color: #7777aa; background: #111122; }
#sca-panel .sca-speed-btn.active-slow { border-color: #4ade80; color: #4ade80; background: #0d1f12; }
#sca-panel .sca-speed-btn.active-medium { border-color: #facc15; color: #facc15; background: #1a1800; }
#sca-panel .sca-speed-btn.active-fast { border-color: #f87171; color: #f87171; background: #1f0d0d; }
#sca-panel .sca-ms { font-size: 10px; opacity: 0.5; }
#sca-panel .sca-drag { margin-top: 12px; font-size: 9px; color: #222233; letter-spacing: 0.08em; text-align: center; }
`;
document.head.appendChild(style);
const panel = document.createElement('div');
panel.id = 'sca-panel';
panel.innerHTML = `
<div class="sca-header"><div class="sca-dot" id="sca-dot"></div>PIXEL ACCELERATOR</div>
<div class="sca-status" id="sca-status">Why Winfarm...</div>
<div class="sca-btn-row">
<button class="sca-toggle on" id="sca-toggle">ON</button>
<button class="sca-auto" id="sca-auto">AUTO</button>
</div>
<div class="sca-speeds">
<button class="sca-speed-btn" data-speed="slow">SLOW <span class="sca-ms">12ms</span></button>
<button class="sca-speed-btn" data-speed="medium">MEDIUM <span class="sca-ms">9ms</span></button>
<button class="sca-speed-btn" data-speed="fast">FAST <span class="sca-ms">5ms</span></button>
</div>
<div class="sca-drag">drag to move · auto: Shift+K · hide: Shift+N</div>
`;
document.body.appendChild(panel);
let dragging = false, dragOffX = 0, dragOffY = 0;
panel.addEventListener('mousedown', (e) => {
if (e.target.tagName === 'BUTTON') return;
dragging = true;
dragOffX = e.clientX - panel.getBoundingClientRect().left;
dragOffY = e.clientY - panel.getBoundingClientRect().top;
panel.style.cursor = 'grabbing';
});
document.addEventListener('mousemove', (e) => {
if (!dragging) return;
panel.style.right = 'auto'; panel.style.bottom = 'auto';
panel.style.left = (e.clientX - dragOffX) + 'px';
panel.style.top = (e.clientY - dragOffY) + 'px';
});
document.addEventListener('mouseup', () => { dragging = false; panel.style.cursor = 'default'; });
function updateUI() {
const toggleBtn = document.getElementById('sca-toggle');
const autoBtn = document.getElementById('sca-auto');
const dot = document.getElementById('sca-dot');
const status = document.getElementById('sca-status');
if (!toggleBtn) return;
toggleBtn.textContent = enabled ? 'ON' : 'OFF';
toggleBtn.classList.toggle('on', enabled);
autoBtn.textContent = autoMode ? 'AUTO ●' : 'AUTO';
autoBtn.classList.toggle('on', autoMode);
const wsReady = hookedSocket && hookedSocket.readyState === 1;
status.style.color = wsReady ? '#38bdf8' : '#555577';
if (!enabled) dot.className = 'sca-dot';
else if (isPlacing()) dot.className = 'sca-dot placing';
else dot.className = 'sca-dot standby';
}
function updateSpeedUI() {
document.querySelectorAll('.sca-speed-btn').forEach(btn => {
btn.className = 'sca-speed-btn' + (btn.dataset.speed === currentSpeed ? ` active-${btn.dataset.speed}` : '');
});
}
setInterval(updateUI, 1000);
document.getElementById('sca-toggle').addEventListener('click', (e) => {
e.stopPropagation();
enabled = !enabled;
if (!enabled) { autoMode = false; spaceHeld = false; stopLoops(); }
syncPlacing();
});
document.getElementById('sca-auto').addEventListener('click', (e) => {
e.stopPropagation();
if (!enabled) return;
autoMode = !autoMode;
syncPlacing();
});
document.querySelectorAll('.sca-speed-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
e.stopPropagation();
currentSpeed = btn.dataset.speed;
updateSpeedUI();
if (wsLoop) { wsLoop.stop(); wsLoop = null; if (isPlacing() && hookedSocket) wsLoop = makeLoop(wsTick, WS_INTERVAL); }
updateUI();
});
});
updateSpeedUI();
updateUI();
})();