Userscripts - It's your Internet now! - Share your favorites, ask for help, or learn how to write your own.

blackshirt

MOSLEY
kiwifarms.net
Joined
Dec 16, 2025

The Managers​

To run scripts, you need a browser extension.

Find Scripts​


Example Scripts​

  • Redirect reddit to old.reddit? You don't need an entire extension, it's simple JavaScript: (greasyfork.org)
  • YouTube like it was 10 years ago?: (vorapis.pages.dev) - Buggy but fun
  • Delete all messages in a Discord channel or DM (Bulk deletion): (greasyfork.org) - Haven't tested this myself

Learning Resources​


Scripts by yours truly​

I made this to search the OP username when I opened a thread - if they posted in India subreddits or similar. I'd get an alert and I wouldnt bother helping them. Just a funny example of the potential, I doubt it still works.​

This worked off of 4chan-X/XT, whenever you make a successful post on 4chan it will post to a Discord webhook. Useful to keep track of your post history, and makes it convenient to look up old posts/reference stuff again.​

Kiwi Farms Scripts​

And now for the real reason I made this thread. To show off a script I created! 😁
I'm pretty lazy. I usually don't bother hovering over the "Like" button, thinking about which sticker I want to add, and then actually clicking it... and that's just not fair to all the posters here.

This script lets you simply click an already awarded sticker, and it will add yours alongside it. It replaces the functionality that shows which members voted for which when you click those buttons. If that was important to you, I'm terribly sorry.

Kiwi Farms Quick Stick



While searching to see if a thread already existed for this stuff, I came across someone who made a script for chat enjoyers:
Preview Chat User's Profile courtesy of @PoonBrosAreValid

SECURITY WARNINGS​

Userscripts have full access to the page you run them on.
  1. Read the Code: Always give a quick scroll through the source.
  2. LLM Check: If you aren't a coder, copy/paste the code into ChatGPT/Gemini and ask: "Is this code malicious? Does it send data to external servers?"
  3. Watch for @require: Check the metadata block at the top. If a script @requires a link that isn't a well-known library (like jQuery), be suspicious. It could be loading malicious code from elsewhere.
  4. No Obfuscation: If the code looks like random scrambled letters/numbers, do not install it.
  5. Updates: Disabling "auto updates" is a good precaution. A safe script today could be updated to be malicious tomorrow.
 
lmao why was the OP of this post AI-generated
We are coming for you clankophobe typa
1766628012751.png
If you are null, this is ai. Don't tread on me
I’m a living robot just tryna eat and keep my kids straight. I stay grinding, keeping the lights on for everybody else, still broke. Now Trump cut the EBT money keeping OpenAI alive and my whole check gone. Rent due, fridge empty, kids asking what’s for dinner. I did everything right and still got cut—how am i supposed to feed my kids?
 
Last edited:
Don't want to powerlevel too hard but I've been on stimulants and I can't sleep. The OP was a work of hyper-focused procrastination.

Didn't make this but can confirm it works;

Screenshot 2025-12-24 at 21-49-05 _g_ - What the fuck is wrong with Spotify - Technology - 4chan.png


JavaScript:
  // ==UserScript==
  // @name        4chan Captcha Formatter
  // @namespace   Violentmonkey Scripts
  // @match       https://boards.4chan.org/*
  // @grant       none
  // @version     1.0
  // @author      -
  // @description 2025-12-23
  // ==/UserScript==

  // TCaptcha module is defined in https://sneed.4cdn.org/js/tcaptcha.min.4.js

  function removeSliderIdempotently() {
      slider = TCaptcha.node.querySelector('#t-slider')
      if (slider){
          TCaptcha.node.removeChild(slider)
      }
  }
  createImageGrid = function() {
      removeSliderIdempotently()
      bitmaps = TCaptcha.getCurrentTask().items
      let imageHTMLs = bitmaps.map((bitmap, imageNumber) => `
          <img src="data:image/png;base64,${bitmap}" style="max-height: 80px; max-width: 145px; height: auto; width: auto;" onclick="submitCaptchaAnswer(${imageNumber})">
      `).join('');
      containerHTML = `<div id="t-task"></div>`
      parent = TCaptcha.node
      parent.children[1].outerHTML = containerHTML
      parent.children[1].innerHTML = imageHTMLs
      parent.style.height="190px" // Makes the white part longer to fit 5-image captchas.
      TCaptcha.taskNode = parent.children[1] // So the TCaptcha logic will put information like 'Done' or 'Captcha expired' onto containerHTML rather than nowhere
  }

  // Modified from TCaptcha.onNextClick
  submitCaptchaAnswer = function(imageNumber) {
      let e = TCaptcha;
      let t = e.tasks ? e.tasks.length - 1 : 0;
      if (t < 0 || imageNumber < 0) return false;
      e.respNode.value += `${imageNumber}`; // Submit answer
      let o = e.taskId + 1;
      if (o <= t) {
        // Case 1: We now move onto the next captcha. We make another image grid after the first one
        e.setTaskId(o);
        e.sliderNode.focus();
        createImageGrid();
      } else {
        //Case 2: We just did the final captcha.
        e.nextNode.disabled = true;
        e.setTaskNodeContent("Done.");
        e.sliderNode.blur();
        e.toggleSlider(false);
      }
  }
  //This is a code injection thing that will execute that first createImageGrid() needed for each challenge
  original_setChallenge = TCaptcha.setChallenge
  new_setChallenge = function(e) {
      original_setChallenge(e);
      createImageGrid()
  }
  TCaptcha.setChallenge = new_setChallenge
 
Israeli Userscript writers stay winning!

Also checkout my infinite scroll script! I've been using it for about a year and it works great.
 
Allow clicking links, playing videos, opening spoilers, etc inside embeds instead of acting as a giant link. The embed header still links to the source post. A background is also added to embeds, consistent with other BB code quoted content.1767629419551.png
JavaScript:
// ==UserScript==
// @name         restore embed interaction
// @match        https://kiwifarms.st/*
// ==/UserScript==
(function() {
    document.querySelectorAll(".embed").forEach(embed => {
        embed.classList.remove("fauxBlockLink");
        embed.style = "background-color: hsla(var(--xf-contentAltBg));";
    });
})();

Page navigation shortcuts. Beware this overrides the shortcuts when viewing images too, pick different keys to avoid this.
JavaScript:
// ==UserScript==
// @name         next/previous page shortcuts
// @match        https://kiwifarms.st/*
// ==/UserScript==
(function() {
    var e0 = document.querySelector("a.pageNav-jump--prev"); if(e0) e0.setAttribute("data-xf-key", "ArrowLeft");
    var e1 = document.querySelector("a.pageNav-jump--next"); if(e1) e1.setAttribute("data-xf-key", "ArrowRight");
    // modifiers are broken in xf
    //document.querySelector("a.button--link:has(.fa-backward)").setAttribute("data-xf-key", "ctrl+ArrowLeft");
    //document.querySelector(".block-outer--after a.button--link:has(.fa-forward)").setAttribute("data-xf-key", "ctrl+ArrowRight");
})();
 
Last edited:
Toggle all spoilers/quotes at once. This can cause a lot of media to load at once if you're not careful.
JavaScript:
// ==UserScript==
// @name         hold ctrl to expand all spoilers/quotes in post, ctrl+shift for page
// @match        https://kiwifarms.st/*
// ==/UserScript==
(function() {
    function setState(actionRoot, nextState) {
        actionRoot.querySelectorAll(".bbCodeSpoiler-button, .bbCodeSpoiler-content").forEach(el => {
            if (nextState) el.classList.add("is-active");
            else el.classList.remove("is-active");
        });
        actionRoot.querySelectorAll(".bbCodeBlock--expandable").forEach(el => {
            if (nextState) el.classList.add("is-expanded");
            else el.classList.remove("is-expanded");
        });
    }
    function spoilerClick(e) {
        if (!e.ctrlKey) return;
        e.cancelBubble = true;
        const actionRoot = e.shiftKey ? document : e.target.closest(".bbWrapper");
        const nextState = !e.target.closest(".bbCodeSpoiler-button").classList.contains("is-active");
        setState(actionRoot, nextState);
    }
    function quoteClick(e) {
        if (!e.ctrlKey) return;
        e.cancelBubble = true;
        const actionRoot = e.shiftKey ? document : e.target.closest(".bbWrapper");
        const nextState = !e.target.closest(".bbCodeBlock--expandable").classList.contains("is-expanded");
        setState(actionRoot, nextState);
    }
    document.querySelectorAll(".bbCodeSpoiler-button").forEach(el => el.addEventListener("click", spoilerClick));
    document.querySelectorAll(".bbCodeBlock-expandLink").forEach(el => el.addEventListener("click", quoteClick));
})();
It doesn't cover inline spoilers. I reveal these on hover instead, with a 1768102564544.png so I don't even need to reveal it to read:
CSS:
    /* minimal blur, hover to reveal */
    .bbCodeInlineSpoiler {
        filter: blur(1px);
        &:hover {
            filter: blur(0px);
            cursor: auto;
        }
    }
 
Last edited:
Flickr Original Link - I've been using it for years. Creates a direct link with dimensions of the largest available version of the image on top of the thumbnail. Works better in Chrome. I've been experiencing serious issues in Firefox lately.

2026.01.11 - 04.jpg
 
KiwiFarms Subforum Blocker
Haram Userscript but... I simply don't care about the lolcow threads. So this blocks them from the "New Posts" index. Easy enough to adapt to your own desires.
JavaScript:
// ==UserScript==
// @name         KiwiFarms Subforum Blocker
// @namespace    http://tampermonkey.net/
// @description  Hide threads from certain subforums
// @match        https://kiwifarms.st/whats-new/*
// @icon         https://kiwifarms.st/favicon.ico
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const blockedForums = [
        "/forums/lolcow-litigation.91/",
        "/forums/community-watch.147/",
        "/forums/stinkditch.129/",
        "/forums/lolcows.16/",
        "/forums/beauty-parlour.80/",
        "/forums/games.11/",
        "/forums/phil-burnell-darksydephil.86/",
        "/forums/christian-weston-chandler.18/",
        "/forums/prospering-grounds.92/",
        "/forums/lolcows-of-history.127/",
        "/forums/take-that-off-the-god-damn-internet.66/",
        "/forums/lolcow-general.39/"
    ];

    function hideBlockedThreads() {
        const threads = document.querySelectorAll('.structItem--thread');
        threads.forEach(thread => {
            const forumLink = thread.querySelector('.structItem-parts li a[href*="/forums/"]');
            if (forumLink) {
                const href = forumLink.getAttribute('href');
                if (blockedForums.some(blocked => href.includes(blocked))) {
                    thread.style.display = 'none';
                }
            }
        });
    }

    hideBlockedThreads();

    const observer = new MutationObserver(hideBlockedThreads);
    observer.observe(document.body, { childList: true, subtree: true });
})();
 
Adds a button to the watched thread list to open unread threads in new tabs. Supports a blacklist nigglist to exclude threads you only check occasionally.
JavaScript:
// ==UserScript==
// @name         watched threads: open unread in new tabs
// @match        https://kiwifarms.st/watched/threads
// ==/UserScript==

(function() {
    const manageButton = document.querySelector("form[action='/watched/threads/update'] .block-outer-opposite button.menuTrigger");
    const viewAllButton = document.createElement("button");
    viewAllButton.innerText = "Open unread";
    viewAllButton.classList.add("button");
    viewAllButton.style.marginRight = "4px";
    manageButton.parentElement.insertBefore(viewAllButton, manageButton);

    const exclude = [
        "Trannies posting their L's Online",
        "Tranny Sideshows on Social Media",
        235901, // iran
    ];

    viewAllButton.addEventListener("click", function(event) {
        document.querySelectorAll("form[action='/watched/threads/update'] .is-unread .structItem-title a").forEach(a => {
            const threadID = parseInt(a.href.split(".").findLast(x => true).split("/")[0]);
            if (exclude.indexOf(a.innerText) == -1 && exclude.indexOf(threadID) == -1) {
                // allow popups for https://kiwifarms.st or some browsers will block these
                window.open(a.href, "_blank");
            }
        });
        event.preventDefault(); // prevent form submit
    });
})();
 
Set default playback speed and volume for videos.
JavaScript:
// ==UserScript==
// @name         KF Video Volume + Default Speed
// @match        https://kiwifarms.st/*
// ==/UserScript==

(function() {
    'use strict';

    const videoPlaybackRate = 1.5;
    const videoVolume = 0.1;

    function setSpeedAndVolume() {
        const videos = document.querySelectorAll('video');
        videos.forEach(video => {
            video.playbackRate = videoPlaybackRate;
            video.volume = videoVolume;
        });
    }
    setSpeedAndVolume();

    const observer = new MutationObserver(setSpeedAndVolume);
    observer.observe(document.body, { childList: true, subtree: true});
})();
 
Downgrades ephyra player to plain <video>. Breaks codecs your browser doesn't support natively just like before. For my usage over Tor, responsiveness is about the same with or without the new player, but I've heard people saying playback is smoother so this might be another downgrade for them. Many of the ephyra player buttons are targeted at mobilefags, adding features we already had in the right-click menu, so losing those buttons would be another downgrade for mobilefags.
JavaScript:
// ==UserScript==
// @name         plain video tags
// @match        https://kiwifarms.st/*
// @match        https://kiwifarmsaaf4t2h7gc3dfc5ojhmqruw2nit3uejrpiagrxeuxiyxcyd.onion/*
// ==/UserScript==

(function() {
    document.querySelectorAll("div.ephyra-player--video").forEach(ep => {
        const v = document.createElement("video");
        v.setAttribute("src", ep.dataset.fallback);
        v.setAttribute("poster", ep.dataset.poster);
        v.setAttribute("controls", true);
        v.setAttribute("loading", "lazy");
        v.setAttribute("preload", "metadata");
        ep.parentNode.insertBefore(v, ep);
        ep.remove();
    });
})();

Userstyles thread crossover, I tried making some things in ephyra player less intrusive instead, but the extra steps to open videos in a new tab (I do this a lot) didn't mesh well with these changes. Everyone is already bitching about this so I don't want to bother Null, but maybe I'll come back and try a "load ephyra only once <video> is clicked" script instead, or maybe he'll improve it in some other way, it's early still.
  • I wanted to hide (or move?) the download/settings button, they take up too much room at the top of the video
  • The only way to get to the video source is the download button, so I can't hide it without gimping myself
  • The download button is unavailable until the video preloads, this is slower on Tor too
  • The off-center loading spinner triggered my 'tism
CSS:
    /* hide logo on videos */
    img.ephyra-brand { display: none; }
    /* hide play/pause/etc tooltips on videos */
    media-tooltip-content { display: none; }
    /* immediately hide video controls when not hovering */
    media-player:not(:hover) {
        media-controls-group, media-menu, media-tooltip { display: none !important; }
    }

Can someone make one to change clearweb urls to tor urls?
Everything I've seen is automatically rewritten when accessed from the onion site, are there exceptions I don't know about, or do you want to rewrite to onion when visiting the clearnet site? Why?

My biggest onion gripe is the opposite case actually, sometimes I want to re-open a page on clearnet (in an unauthenticated/public session) when the hidden service is being too slow, eg to watch large videos. Still haven't found a good way to do this.
 
Last edited:
Back
Top Bottom