[Userscript] WaniKani Dashboard SRS and Leech Breakdown

Do you have any burned leeches? This script doesn’t show burned leeches.

1 Like

Yup, was the burned leeches. I didn’t know they’d be counted too. Thanks!

1 Like

You can get the burned leeches out of Self-Study Quiz if you setup the SRS Level filter on top of Leech Training to exclude them. Just select all SRS Levels except burn.


Can I just say how glad I am that I finally found this?
I had the old unsupported script for so long without knowing why it didn’t work.

Thanks so much for making this @seanblue.


Where does the image on the bottom right come from? Is it Tohru?


Here she is!


It is from @Kumirei woah Burn script.


I mixed the names up, so embarrassing!

1 Like


This is kind of an absurd question, but how can I identify the leeches? I see the number is mapped by our function, mapItemsToSrs, but that simply returns an array with our various SRS level and the numbers of items. While that is useful, I’d like to know which specific items are leeches. To that end I’ve simply modified the script and dumped items identified in isLeech into an array and console logged them for my own info. If there’s a script that’s designed to do this, please let me know?

Otherwise, if such a script doesn’t exist, I don’t mind developing it myself. Can display the items in some kind of modal from the menu.

You should be able to get the specific leeches from this script: [Userscript] WaniKani Open Framework Additional Filters (Recent Lessons, Leech Training, Related Items, and more)

It was built specifically to work with the Self Study Quiz script, but in theory it should work with other scripts as well. If Self Study Quiz doesn’t give you what you want, maybe someone has a script that just lists out the items indicated by filters like the leech filter.

This script list the leeches in a table on your dashboard.

1 Like


What do the numbers actually mean? lol

It’s the number of leeches in the four levels of Apprentice and the total.

I’ve made a modification to the script to make it work in light of the new detailed item type breakdown panels that have replaced the old ones:

Updated Script
// ==UserScript==
// @name          WaniKani Dashboard SRS and Leech Breakdown
// @namespace     https://www.wanikani.com
// @description   Show SRS and leech breakdown on dashboard
// @author        seanblue
// @version       1.0.0
// @include       https://www.wanikani.com/dashboard
// @include       https://www.wanikani.com/
// @grant         none
// ==/UserScript==

(function() {
    'use strict';

	if (!window.wkof) {
		let response = confirm('WaniKani Dashboard SRS and Leech Breakdown script requires WaniKani Open Framework.\n Click "OK" to be forwarded to installation instructions.');

		if (response) {
			window.location.href = 'https://community.wanikani.com/t/instructions-installing-wanikani-open-framework/28549';


     let style =
.srs-progress-details {
	position: relative;
	color: #fff;
    padding-top: 0.3em;

.dashboard section.srs-progress span {
    margin-bottom: 0.2em;

.dashboard section.srs-progress .srs-progress-details .leech-count .leech-breakdown {
    background-color: black;
    font-size: 0.8em;
    font-weight: 100;
    opacity: 0.75;
    display: none;

.dashboard section.srs-progress li:hover .srs-progress-details .leech-count .leech-breakdown {
    display: inline;

.dashboard section.srs-progress .srs-progress-details .leech-count {
    background-color: black;
    position: absolute;
    right: -0.7em;
    padding-left: 0.3em;
    padding-right: 0.3em;
    font-size: 1em;
    opacity: 0.25;
    font-weight: 100;

.dashboard section.srs-progress li:hover .srs-progress-details .leech-count {
    opacity: 1.0;


	const leechThreshold = 1;
	const config = {
		wk_items: {
			options: {
				review_statistics: true,
				assignments: true


	function getItems(items) {
		return wkof.ItemData.get_items(config).then(filterToActiveAssignments);

	function filterToActiveAssignments(items) {
		return items.filter(itemIsActiveAssignment);

	function itemIsActiveAssignment(item) {
		let assignments = item.assignments;
		if (assignments === undefined) {
			return false;

		let srsStage = getSrsStage(assignments);

		return srsStage >= 1 && srsStage <= 8;

	function getSrsStage(assignments) {
		return assignments.srs_stage;

	function mapItemsToSrs(items) {
		let itemsBySrs = [1, 2, 3, 4, 5, 6, 7, 8].reduce((result, srs) => {
			result[srs] = {
				total: 0,
				leech: 0

			return result;
		}, {});

		items.forEach(function(item) {
			let srsStage = getSrsStage(item.assignments);

			if (isLeech(item)) {

		return itemsBySrs;

	function isLeech(item) {
		if (item.review_statistics === undefined) {
			return false;

		let reviewStats = item.review_statistics;
		let meaningScore = getLeechScore(reviewStats.meaning_incorrect, reviewStats.meaning_current_streak);
		let readingScore = getLeechScore(reviewStats.reading_incorrect, reviewStats.reading_current_streak);

		return meaningScore >= leechThreshold || readingScore >= leechThreshold;

	function getLeechScore(incorrect, currentStreak) {
		return incorrect / Math.pow((currentStreak || 0.5), 1.5);

	function updatePage(itemsBySrs) {
		displayDetailedSection('apprentice', itemsBySrs, [1, 2, 3, 4]);
		displayDetailedSection('guru', itemsBySrs, [5, 6]);
		displaySimpleSection('master', itemsBySrs, 7);
		displaySimpleSection('enlightened', itemsBySrs, 8);

	function displayDetailedSection(srsSectionId, itemsBySrs, srsLevelsArray) {
		let srsProgressDetailsSection = addFilledTotalBreakdownSection(srsSectionId, itemsBySrs, srsLevelsArray);
		addDetailedLeechSection(srsProgressDetailsSection, itemsBySrs, srsLevelsArray);

	function displaySimpleSection(srsSectionId, itemsBySrs, srsLevel) {
		let srsProgressDetailsSection = addEmptyTotalBreakdownSection(srsSectionId);
		addSimpleLeechSection(srsProgressDetailsSection, itemsBySrs, srsLevel);

	function displayEmptySection(srsSectionId) {

	function addFilledTotalBreakdownSection(srsSectionId, itemsBySrs, srsLevelsArray) {
		let totals = srsLevelsArray.map(srs => itemsBySrs[srs].total).join('&nbsp;/&nbsp;');
		return addTotalBreakdownSection(srsSectionId, `${totals}`);

	function addEmptyTotalBreakdownSection(srsSectionId) {
		return addTotalBreakdownSection(srsSectionId, '&nbsp;');

	function addTotalBreakdownSection(srsSectionId, sectionContent) {
		let section = $(`<div class="srs-progress-details">${sectionContent}</div>`);
		$(`.srs-progress__stage--${srsSectionId} .srs-progress__subject-types`).after(section);

		return section;

	function addDetailedLeechSection(srsProgressDetailsSection, itemsBySrs, srsLevelsArray) {
		let leechArray = srsLevelsArray.map(srsLevel => itemsBySrs[srsLevel].leech);
		let leechBreakdown = leechArray.join('&nbsp;/&nbsp;');
		let leechTotal = leechArray.reduce((total, val) => total + val, 0);

		let sectionContent = `<span class="leech-breakdown">(${leechBreakdown})&nbsp;</span>${leechTotal}`;

		addLeechSection(srsProgressDetailsSection, sectionContent);

	function addSimpleLeechSection(srsProgressDetailsSection, itemsBySrs, srsLevel) {
		addLeechSection(srsProgressDetailsSection, `${itemsBySrs[srsLevel].leech}`);

	function addLeechSection(srsProgressDetailsSection, sectionContent) {
		let section = `<span class="leech-count">${sectionContent}</span>`;


It’s a bit of a hack so it looks unpolished - some of the alignment for the leech panel isn’t great, but basically I just updated the selectors and adjusted the positioning a little.


thank you!

I don’t have the last line with the leech counts. How can I have it back?

You need to go to the edit script window in tampermonkey and replace the contents of the script with the one I shared in the post.

Yes that worked, but apparently you find more leeches than before. A week ago I had about 140 leeches, now I suddenly have 217. Is the threshold for ‘leech or not leech’ configurable somewhere?

EDIT: That one was easy. I changed line 67 to
const leechThreshold = 2;
and I am back to my usual numbers:

I haven’t changed anything related to leech calculation, so I’m not sure there. Maybe something reset your settings and you had an older version before?

It’s possible that I changed the leech definition in the script long ago and that this change was now overwritten by the update. No worries :slightly_smiling_face: