Greasy Fork is available in English.
Optimize Agar.io performance and blocking ads.
// ==UserScript==
// @name Enchanted Agar.io
// @icon https://i.imgur.com/sjYtMDN.png
// @namespace https://greatest.deepsurf.us/users/1372128
// @version 1.4
// @description Optimize Agar.io performance and blocking ads.
// @author Dragon9135
// @match *://agar.io/*
// @run-at document-start
// @grant none
// @license Custom License
// ==/UserScript==
// Copyright (C) 2026 Dragon9135
// This software cannot be modified, copied, or redistributed in any form.
(function() {
'use strict';
// ─── Constants ────────────────────────────────────────────────────────────────
const SPLIT_DELAY = 25;
const AD_SELECTORS = [
'iframe[src*="ads"]', 'iframe[src*="adserver"]',
'iframe[src*="doubleclick"]', 'iframe[src*="googlesyndication"]',
'[id*="adBanner"]', '[id*="adContainer"]', '[id*="ad-container"]',
'[class*="adBox"]', '[class*="ad-container"]',
'[id*="google_ads"]', '[class*="google_ads"]',
'[id*="agar-io_300x250"]', '[class*="agar-io_300x250"]',
'[id*="agar-io_160x600"]', '[class*="agar-io_160x600"]',
'[id*="google_ads_iframe"]', '[class*="google_ads_iframe"]',
'[id*="agar-io_160x600_2"]', '[class*="agar-io_160x600_2"]',
'[id*="agar-io_970x90"]', '[class*="agar-io_970x90"]',
'#preroll', '.preroll', '#adsBottom', '.adsBottom',
'[id^="google_ads"]',
'[id*="divFullscreenLoading"]', '[class*="divFullscreenLoading"]'
].join(',');
// ─── WASM Binary Patch ───────────────────────────────────────────────────────
function findPattern(buffer, pattern) {
for (let i = 0; i <= buffer.length - pattern.length; i++) {
let match = true;
for (let j = 0; j < pattern.length; j++) {
if (buffer[i + j] !== pattern[j]) {
match = false;
break;
}
}
if (match) return i;
}
return -1;
}
function concatUint8Arrays(arrays) {
const total = arrays.reduce((s, a) => s + a.length, 0);
const result = new Uint8Array(total);
let offset = 0;
for (const a of arrays) {
result.set(a, offset);
offset += a.length;
}
return result;
}
function readULEB(buffer, offset) {
let result = 0,
shift = 0,
pos = offset;
while (pos < buffer.length) {
const byte = buffer[pos++];
result |= (byte & 0x7f) << shift >>> 0;
if ((byte & 0x80) === 0) break;
shift += 7;
}
return {
value: result >>> 0,
length: pos - offset
};
}
function writeULEB(value) {
const out = [];
let v = value >>> 0;
do {
let byte = v & 0x7f;
v >>>= 7;
if (v !== 0) byte |= 0x80;
out.push(byte);
} while (v !== 0);
return new Uint8Array(out);
}
function fixSectionSizeByDelta(u8, sectionId, delta) {
if (delta === 0) return u8;
if (u8[0] !== 0x00 || u8[1] !== 0x61 || u8[2] !== 0x73 || u8[3] !== 0x6d) return u8;
let i = 8;
while (i < u8.length) {
const sid = u8[i++];
const sizeInfo = readULEB(u8, i);
const sizeStart = i,
sizeLen = sizeInfo.length;
const payloadSize = sizeInfo.value >>> 0;
if (sid === sectionId) {
const newSize = payloadSize + delta;
if (newSize < 0) return u8;
const newSizeBytes = writeULEB(newSize);
return concatUint8Arrays([u8.slice(0, sizeStart), newSizeBytes, u8.slice(sizeStart + sizeLen)]);
}
i += sizeLen + payloadSize;
}
return u8;
}
function applyPatch(u8, operations) {
let result = u8;
const initialLength = u8.length;
let anyFail = false;
for (const {
pattern,
payload,
type
}
of operations) {
const index = findPattern(result, pattern);
if (index === -1) {
console.warn('[Enchanted] WASM pattern not found:', pattern.map(b => b.toString(16)).join(' '));
anyFail = true;
continue;
}
const pi = index + pattern.length;
if (type === 'insertAfter') {
result = concatUint8Arrays([result.slice(0, pi), new Uint8Array(payload), result.slice(pi)]);
} else if (type === 'replaceAfter') {
result = concatUint8Arrays([result.slice(0, pi), new Uint8Array(payload), result.slice(pi + payload.length)]);
} else if (type === 'replaceUlebAfter') {
const {
value: oldVal,
length: oldLen
} = readULEB(result, pi);
const newBytes = writeULEB(oldVal + (result.length - initialLength));
result = concatUint8Arrays([result.slice(0, pi), newBytes, result.slice(pi + oldLen)]);
}
}
return anyFail ? u8 : result;
}
function patchWasm(buffer) {
const bytes = hex => hex.split(' ').map(b => parseInt(b, 16));
const original = new Uint8Array(buffer);
const patched = applyPatch(original, [{
pattern: bytes('D4 01 2D 00 00 45 0D 00 20 02 10 0F 20 01 20 02 10 1E 21 01'),
payload: bytes('20 00 28 02 1C 45 04 40 0F 0B'),
type: 'insertAfter'
},
{
pattern: bytes('00 0B 37 03 00 20 00 20 04 37 03 08 20 03 41 10 6A 24 00 0B'),
payload: bytes('8A'),
type: 'replaceAfter'
},
{
pattern: bytes('41 1B 6C 41 01 6A 73 3A 00 07 20 1F BF 44 00 00 00 00 00 00'),
payload: bytes('00 00'),
type: 'replaceAfter'
}
]);
const delta = patched.length - original.length;
return (delta === 0 ? patched : fixSectionSizeByDelta(patched, 0x0a, delta)).buffer;
}
// Intercept agario.core.js before it's added to DOM, apply WASM patch
function initCoreInterceptor() {
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (node.nodeName !== 'SCRIPT') continue;
const src = node.src || '';
if (!src.match(/agario\.core\.js/i)) continue;
observer.disconnect();
node.remove();
const xhr = new XMLHttpRequest();
xhr.open('GET', src, true);
xhr.responseType = 'text';
xhr.onload = () => {
const origInstantiate = WebAssembly.instantiate;
WebAssembly.instantiate = function(bufferOrModule, importObject) {
if (bufferOrModule instanceof ArrayBuffer) {
bufferOrModule = patchWasm(bufferOrModule);
}
return origInstantiate.call(this, bufferOrModule, importObject);
};
const blob = new Blob([xhr.responseText], {
type: 'text/javascript'
});
const blobURL = URL.createObjectURL(blob);
const script = document.createElement('script');
script.src = blobURL;
script.onload = () => URL.revokeObjectURL(blobURL);
// document.body might not exist yet at document-start
(document.body || document.documentElement).appendChild(script);
console.log('[Enchanted] agario.core.js intercepted, WASM patch applied.');
};
xhr.onerror = () => {
(document.body || document.documentElement).appendChild(node);
console.warn('[Enchanted] Core intercept failed, loading original...');
};
xhr.send();
return;
}
}
});
observer.observe(document, {
childList: true,
subtree: true
});
}
// ─── FPS Limit Removal ─────────────────────────────────────────────────────
// Caught when core is set, setFpsCap is always called with -1
function initFpsCap() {
let _core;
Object.defineProperty(window, 'core', {
get() {
return _core;
},
set(value) {
_core = value;
setTimeout(() => {
if (typeof _core?.setFpsCap !== 'function') return;
const orig = _core.setFpsCap;
_core.setFpsCap = () => orig(-1);
orig(-1);
console.log('[Enchanted] FPS limit removed.');
// Enable the Minimap
enableMinimap(_core);
}, 0);
},
configurable: true,
});
}
// Run immediately at document-start
initCoreInterceptor();
initFpsCap();
// ─── Minimap ─────────────────────────────────────────────────────────────────
function enableMinimap(coreObj) {
if (typeof coreObj !== 'undefined') {
if (typeof coreObj.setMinimap === 'function') {
coreObj.setMinimap(1);
console.log('[Enchanted] Minimap: It worked successfully.');
} else {
console.warn('[Enchanted] core.setMinimap function could not be found.');
}
if (typeof coreObj.playersMinimap === 'function') {
coreObj.playersMinimap(1);
}
}
}
// ─── Ad Removal (DOM) ────────────────────────────────────────────────────────
let adCheckScheduled = false;
function scheduleAdCheck() {
if (adCheckScheduled) return;
adCheckScheduled = true;
requestIdleCallback(() => {
removeAds();
adCheckScheduled = false;
}, {
timeout: 500
});
}
function removeAds() {
document.querySelectorAll(AD_SELECTORS).forEach(el => el.remove());
document.documentElement.style.setProperty('--bottom-banner-height', '0px');
}
function adjustBannerHeight() {
document.documentElement.style.setProperty('--bottom-banner-height', '0px');
}
// ─── Advanced Ad Blocking ────────────────────────────────────────────────────
// 1) Disable ads by setting isPayingUser=true
function patchPayingUser() {
document.addEventListener('update_user_info', function(e) {
if (e.detail?.isPayingUser) return;
const detail = Object.assign({}, e.detail, {
isPayingUser: true
});
e.stopPropagation();
document.dispatchEvent(new CustomEvent('update_user_info', {
detail
}));
}, true);
}
// 2) Make agarApp.ads API no-op
function patchAgarAppAds() {
const noop = () => {};
const adsPatch = {
requestAds: noop,
requestAd: noop,
refreshAd: noop,
destroyAd: noop,
adSlots: noop,
enableTargetedAds: noop,
disableTargetedAds: noop,
isTargeted: noop,
supersonicAds: {
BrandConnectReadyEvent: noop,
BrandConnectDoneEvent: noop,
BrandConnectOpenEvent: noop,
BrandConnectCloseEvent: noop,
BrandConnectCompletedEvent: noop,
hasEngagement: () => false,
}
};
function applyAdsPatch() {
if (!window['agarApp']) return;
window['agarApp'].ads = Object.assign(window['agarApp'].ads || {}, adsPatch);
}
let _agarApp = window['agarApp'];
Object.defineProperty(window, 'agarApp', {
get() {
return _agarApp;
},
set(value) {
_agarApp = value;
applyAdsPatch();
},
configurable: true,
});
applyAdsPatch();
}
// 3) toggleTargetedAds — crashes when this._ads is undefined, wrap with try/catch
function patchToggleTargetedAds() {
const waitForCore = setInterval(() => {
if (typeof window['core'] === 'undefined') return;
clearInterval(waitForCore);
try {
const proto = Object.getPrototypeOf(window['core']);
if (proto && typeof proto.toggleTargetedAds === 'function') {
const orig = proto.toggleTargetedAds;
proto.toggleTargetedAds = function() {
try {
return orig.apply(this, arguments);
} catch (e) {}
};
}
} catch (e) {}
}, 200);
}
// 4) Patch Vue components upon loading
function findVueNodes(root, cond) {
const results = [];
function walk(node) {
if (!node) return;
if (cond(node)) results.push(node);
(node.$children || []).forEach(walk);
(node.children || []).forEach(walk);
}
walk(root);
return results;
}
function patchVueAdNodes() {
const waitForApp = setInterval(() => {
if (!window['agarApp']?.home) return;
clearInterval(waitForApp);
setTimeout(() => {
const app = window['agarApp'];
findVueNodes(app.home, n => {
const tag = (n.$vnode?.tag) || '';
return tag.includes('-ads') || tag.includes('-promo');
}).forEach(n => {
try {
n.$destroy();
} catch (e) {}
});
findVueNodes(app.home, n => n.elm?.id === 'socialButtons')
.forEach(n => {
try {
n.elm.parentElement.removeChild(n.elm);
} catch (e) {}
});
const adNode = findVueNodes(app.home, n =>
Object.getPrototypeOf(n).hasOwnProperty('hasBottomAd')
)[0];
if (adNode) {
['hasBottomAd', 'hasSideAds'].forEach(prop => {
if (adNode._computedWatchers?.[prop])
adNode._computedWatchers[prop].getter = () => false;
});
Object.defineProperties(adNode, {
hasBottomAd: {
get: () => false,
configurable: true
},
hasSideAds: {
get: () => false,
configurable: true
},
fastEntry: {
get: () => true,
configurable: true
},
});
}
adjustBannerHeight();
}, 500);
}, 500);
}
// ─── Auto Collect Coins ───────────────────────────────────────────────────────
let isLoggedIn = false;
let coinCollectPending = false;
function collectCoins() {
if (!isLoggedIn || coinCollectPending) return;
coinCollectPending = true;
try {
if (window['agarApp']?.API) {
window['agarApp'].API.getFreeCoins();
setTimeout(() => {
try {
window['agarApp'].API.closeTopView();
} catch (e) {}
coinCollectPending = false;
}, 300);
console.log('[Enchanted] Auto Collect Coins: It worked successfully.');
} else {
coinCollectPending = false;
}
} catch (e) {
console.warn('[Enchanted] Auto Collect Coins error:', e);
coinCollectPending = false;
}
}
function initAutoCollectCoins() {
window.addEventListener('login', () => {
if (isLoggedIn) return;
isLoggedIn = true;
coinCollectPending = false;
setTimeout(collectCoins, 500);
});
window.addEventListener('logout', () => {
isLoggedIn = false;
coinCollectPending = false;
});
window.addEventListener('free_coins_timer', () => {
coinCollectPending = false;
collectCoins();
});
}
// ─── UI Customization ────────────────────────────────────────────────────────
function addInstructions() {
const instructions = document.getElementById('instructions');
if (instructions) {
instructions.innerHTML += `
<center><span class='text-muted'>
<span>Press <b>D</b> or <b>2</b> to doublesplit</span><br>
<span>Press <b>3</b> to triplesplit</span><br>
<span>Press <b>T</b> or <b>4</b> to tricksplit</span><br>
<span>Press <b>E</b> to eject macro mass</span>
</span></center>
`;
}
}
function customizeUI() {
const mainUI = document.getElementById('mainui-play');
if (mainUI) mainUI.style.height = '385px';
const nickInput = document.getElementById('nick');
if (nickInput) nickInput.maxLength = 99;
}
// ─── Key Controls ────────────────────────────────────────────────────────────
let isFeedActive = false;
function dispatchKey(keyCode, type) {
window.dispatchEvent(new KeyboardEvent(type, {
keyCode,
bubbles: true
}));
}
function triggerFeed() {
dispatchKey(87, 'keydown');
dispatchKey(87, 'keyup');
}
function triggerSplit() {
dispatchKey(32, 'keydown');
dispatchKey(32, 'keyup');
}
function splitMultiple(times) {
triggerSplit();
for (let i = 1; i < times; i++) setTimeout(triggerSplit, SPLIT_DELAY * i);
}
function macroFeed() {
if (!isFeedActive) return;
triggerFeed();
setTimeout(macroFeed, SPLIT_DELAY);
}
function keydown(event) {
switch (event.keyCode) {
case 69:
if (!isFeedActive) {
isFeedActive = true;
macroFeed();
}
break;
case 84:
case 52:
splitMultiple(4);
break;
case 51:
splitMultiple(3);
break;
case 68:
case 50:
splitMultiple(2);
break;
case 49:
triggerSplit();
break;
}
}
function keyup(event) {
if (event.keyCode === 69) isFeedActive = false;
}
window.addEventListener('keydown', keydown);
window.addEventListener('keyup', keyup);
// ─── MutationObserver ─────────────────────────────────────────────────────────
const domObserver = new MutationObserver((mutations) => {
let hasNewNodes = false;
let overlaysChanged = false;
for (const mutation of mutations) {
if (mutation.addedNodes.length > 0) hasNewNodes = true;
for (const node of [...mutation.addedNodes, ...mutation.removedNodes]) {
if (node.id === 'overlays') overlaysChanged = true;
}
}
if (hasNewNodes) scheduleAdCheck();
});
// ─── Initialization ──────────────────────────────────────────────────────────
function init() {
addInstructions();
customizeUI();
removeAds();
patchPayingUser();
patchAgarAppAds();
patchToggleTargetedAds();
patchVueAdNodes();
initAutoCollectCoins();
domObserver.observe(document.body, {
childList: true,
subtree: true
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();