In a world where technology becomes more utterly fucked every day, userscripts can feel liberating. You don’t know what are userscripts? They’re tiny (or not) javascript scripts that you can inject into any web page. Kinda like a browser plugin, but usually hyper-focused to change specific behaviors or visuals on specific sites.
They’re a great way to take back a bit of control.
You can use them by installing a userscripts manager like Tampermonkey (it’s the one I use).
Now what about a few examples?
Reddit is a mess (to put it gently), you know your health would be better if you stopped interacting with it, but it’s still a useful resource. Rejoice, here’s a script for old.reddit.com to disable the reply button and that useless notification icon!
// ==UserScript==
// @name Better reddit
// @version 2025-07-10
// @description Less annoyances and lower blood pressure
// @author scambier.xyz
// @match https://www.reddit.com/*
// @match https://old.reddit.com/*
// @icon https://www.reddit.com/favicon.ico
// @grant none
// ==/UserScript==
(function() {
'use strict';
setTimeout(() => {
// You don't care about notifications
$('#notifications').next('.separator').remove()
$('#notifications').remove()
$('.message-count.badge-count').remove()
// Read-only mode
$('li.reply-button').remove()
$('div.usertext-edit').remove()
}, 100)
})();itch.io
You like to browse itch.io for quirky games, assets, or inspiration, but you can’t stand the neverending horror and visual novel slops when you’re browsing the site? This script will automatically filter out the horror tag, and dynamically remove visual novels as you scroll down.
// ==UserScript==
// @name Filter games on itch.io
// @version 2024-06-24
// @description Filter out games with a specific tag on itch.io
// @author scambier.xyz
// @match https://itch.io/games
// @match https://itch.io/games?*
// @match https://itch.io/games/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=itch.io
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// Filtering tags only works one tag at a time
const url = new URL(window.location.href);
const exclude = url.searchParams.get("exclude");
if (!exclude) {
url.searchParams.set("exclude", "tg.horror");
window.location.href = url.href;
}
const EXCLUDED_GENRES = [
'visual novel',
];
const removeExcludedGenres = () => {
$('div.game_genre').filter(function() {
const genreText = $(this).text().toLowerCase();
return EXCLUDED_GENRES.some(genre => genreText.includes(genre));
}).closest('div.game_cell').remove();
};
// Observer to also delete when scrolling and loading new games
const observeChanges = () => {
new MutationObserver(removeExcludedGenres).observe(document.body, {
childList: true,
subtree: true
});
};
removeExcludedGenres();
observeChanges();
})();Hackernews
The “orange hell site” is terrible and I don’t visit often it anymore, but I like this snippet: it adds icons to quickly see the links with the most votes or comments.

"use strict";
/* globals jQuery, $, waitForKeyElements */
// ==UserScript==
// @name Hacker News - Most upvoted & most commented links
// @namespace https://github.com/scambier/userscripts
// @downloadURL https://github.com/scambier/userscripts/raw/refs/heads/master/dist/hackernews.user.js
// @updateURL https://github.com/scambier/userscripts/raw/refs/heads/master/dist/hackernews.user.js
// @author scambier.xyz
// @version 0.0.10
// @description Show top 🔥👄 links of Hacker News
// @license ISC
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js
// @match https://news.ycombinator.com/
// @match https://news.ycombinator.com/ask*
// @match https://news.ycombinator.com/news*
// @match https://news.ycombinator.com/show*
// @match https://news.ycombinator.com/front*
// @grant none
// @run-at document-end
// ==/UserScript==
(() => {
// Firefox mobile fix, do nothing if icons are already injected
if ([...$('[data-userscript="scambier"]')].length)
return;
const rows = [...$("tr.athing")];
// Select lines
const items = rows
// Filter out ads
.filter((tr) => $(tr).find("td.votelinks").length)
// Get id and score
.map((tr) => {
var _a, _b;
return {
id: $(tr).attr("id"),
score: Number((_a = $(tr).next().find(".score")[0].textContent) === null || _a === void 0 ? void 0 : _a.split(" ")[0]),
// Weirdly, .split(' ') does not work
comments: Number((_b = $(tr)
.next()
.find('a:contains("comments")')
.text()
.split("comments")[0]
.trim()) !== null && _b !== void 0 ? _b : 0),
};
});
// Top 10% for new entries, top 20% for other pages
const ratio = location.href.includes("news.ycombinator.com/newest")
? 0.1
: 0.2;
// Inject icons
items.forEach((o) => {
const title = $(`tr#${o.id}`).find("span.titleline");
if (getTop(items, "comments", ratio).includes(o) && o.comments > 0) {
title.before('<span title="Most commented" data-userscript="scambier">👄 </span>');
}
if (getTop(items, "score", ratio).includes(o) && o.score > 0) {
title.before('<span title="Most upvoted" data-userscript="scambier">🔥 </span>');
}
});
/**
* Get [ratio] best [items], ordered by [key]
*
* @param items
* @param key
* @param ratio
*/
function getTop(items, key, ratio) {
const count = rows.length;
return [...items].sort((a, b) => b[key] - a[key]).slice(0, count * ratio);
}
})();