Extra Study Mode

Thanks for the heads up! Could you email us at hello@wanikani.com so we can get some more info from you?




I find this script, while not a review tool, helps to surface the items you are referring to so you can give them more looks.

I’m assuming that it would probably not be easy for you to add audio → meaning questions to the extra study mode either as an option with this script or as a separate script?

I imagine it would be too much work, but I just wanted to check in case you could as I find it helps learn words so much more thoroughly :slight_smile:

Nothing much - just having an objective. I always find it easier to do for example 100 reviews if I’ve got that specific objective to work towards, without it I’m much more likely to just stop earlier. But it’s definitely not very important - so probably more trouble than it’s worth!


That’s an interesting motivation hack. I like it.

1 Like

This took me the entire day to create, but here are your two scripts:


After enabling this switch, all extra study sessions will only consist of vocabulary items with audio => meaning questions. Just like the “Extra Study: Burned Items” script, it might not always work reliably due to a racing condition. For now, I also don’t remove vocab items that don’t have audio.


This script changes the number in the top right of the review/extra study page to an input box which allows you to reduce the queue length. You have to confirm the change with the enter key.


That’s amazing!! Thank you so much, I really appreciate it :slight_smile: So far they both seem to work great!


Just made a couple of changes to the Audio Quiz, to add a :loud_sound: (speaker icon) instead of a blank space where the word is, and adding an onclick to it to play the audio when clicking it. No need to merge it into the script if you prefer not to, but in case you do here it is!
You can use text-compare to check the changes :slight_smile:

Updated code
// ==UserScript==
// @name         Extra Study: Audio Quiz
// @namespace    extraStudyAudioQuiz
// @version      1.0
// @description  Adds the option to change the WK extra study to audio => meaning questions.
// @author       Sinyaven
// @license      MIT-0
// @match        https://www.wanikani.com/
// @match        https://www.wanikani.com/dashboard
// @match        https://www.wanikani.com/extra_study/session*
// @match        https://preview.wanikani.com/
// @match        https://preview.wanikani.com/dashboard
// @match        https://preview.wanikani.com/extra_study/session*
// @require      https://greasyfork.org/scripts/441518-wanikani-queue-manipulator/code/WaniKani%20Queue%20Manipulator.js?version=1028290
// @run-at       document-start
// @grant        none
// ==/UserScript==

(function() {
	"use strict";
	/* global $, wkof, wkQueueManipulator */
	/* eslint no-multi-spaces: off */

	if (document.URL.includes("extra_study/session")) {
		if (localStorage.getItem("extraStudyAudioQuiz") !== "true") return;
	} else {

	function onlyVocabQuestions() {

	async function onlyMeaningQuestions() {
		await wkQueueManipulator.jQueryReady();
		$.jStorage.listenKeyChange("questionType", () => {
			if ($.jStorage.get("questionType") !== "meaning") $.jStorage.set("questionType", "meaning");
		$.jStorage.listenKeyChange("currentItem", () => {
			const item = $.jStorage.get("currentItem");
			$.jStorage.set(`e/stats/${item.type[0].toLowerCase()}${item.id}`, {rc: 1});

	async function turnAllIntoAudioQuestions() {
		await wkQueueManipulator.domReady();
        document.getElementById("character").setAttribute('onclick', "document.getElementById('option-audio-player').click()");
		new MutationObserver(m => {
			const answered = m[0].target.classList.contains("correct") || m[0].target.classList.contains("incorrect");
			document.body.classList.toggle("answered", answered);
			if (!answered) document.getElementById("option-audio-player").click();
		}).observe(document.querySelector("#answer-form fieldset"), {attributes: true, attributeFilter: ["class"]});

	async function addToggle() {
		const input = document.createElement("input");
		const label = document.createElement("label");
		input.type = "checkbox";
		label.textContent = "Audio Quiz";
		input.checked = localStorage.getItem("extraStudyAudioQuiz") === "true";
		input.addEventListener("change", e => localStorage.setItem("extraStudyAudioQuiz", e.target.checked));
		await wkQueueManipulator.domReady();
		(await wkQueueManipulator.domElementAvailable(document.getElementsByClassName("extra-study")[0], "div")).after(label);

	async function onlyVocab(list) {
		if (!window.wkof) {
			alert("Extra Study: Audio Quiz script requires Wanikani Open Framework.");
			return list;

		await wkof.ready("ItemData");
		const vocab = await wkof.ItemData.get_items({wk_items: {filters: {item_type: "voc"}}});
		const vocIds = vocab.reduce((result, item) => ((result.add(item.id), result)), new Set());
		return list.filter(id => vocIds.has(id));

	function addDashboardCss() {
		const style = document.createElement("style");
		style.textContent = `
		.extra-study {
			position: relative;

		.extra-study .audio-switch-label {
			position: absolute;
			right: 12px;
			top: 16px;

		.audio-switch-label .switch {
			width: 0;
			margin: 0 1.7em 0 0.4em;
			vertical-align: middle;

		.switch::before {
			content: "";
			position: absolute;
			width: 1.7em;
			height: 1em;
			border-radius: 1em;
			cursor: pointer;
			background-color: #bdbdbd;
			transition: .2s;

		.switch::after {
			content: "";
			position: absolute;
			width: 1em !important;
			height: 1em !important;
			border-radius: 50% !important;
			background-color: white;
			transition: .2s;
			transform: scale(0.8);

		.switch:checked::before {
			background-color: #59c274;

		.switch:checked::after {
			transform: translateX(0.7em) scale(0.8) !important;
            top: 0 !important;
            left: 0 !important;

	function addStudyCss() {
		const style = document.createElement("style");
		style.textContent = `
		#character span {
			opacity: 0;
        #character::after {
            content: "🔊";
            text-align: center;
            position: absolute;
            left: 0;
            right: 0;
            opacity: 1;

        #character:hover {
            cursor: pointer;

		body.answered #character span {
			opacity: 1;
        body.answered #character::after {
			opacity: 0;

Initially I had planned to add a speaker icon, but when I was finished with the basic functionality it was already past midnight, so I released it without this feature. I have looked into your code and added this now (however, I decided to use FontAwesome for the icon, as this is what WK mostly uses). Additionally, Ctrl+J now allows to replay the audio (because when the text input is focused, the usual J hotkey enters the letter “j” instead).

I also saw how hilariously broken the switch is with the Breeze Dark theme. But I will wait for now if this gets fixed in the Breeze Dark theme – I would strongly prefer to not introduce !important into my CSS.

1 Like

Thank you for the great feature but there’s one thing that would put it over the top. Right now, the recent lesson threshold is too high to be useful for me since it hovers around 50 items every day. It would be great to eventually add a setting to limit maybe the last 24-48 hours of additions to that list. It would help alleviate manually hunting for the most recent lessons that were added to the pool to reinforce them.


They mentioned earlier that it is (already) a 24 hour rolling window

1 Like

Recent mistakes is 24hr. Recent lessons is until the “passed” flag is set, which means when it reaches Guru the first time.


Thanks! Your icon is definitely better - I just used the first I found lmao
The only problem with CTRL + J is that in lots of browsers it’s the shortcut for opening the downloads, so maybe ALT + J would be better?
Also, would making a topic in the API category for your 3 extra study scripts be a good idea? That way more people might find them and also any related discussion won’t fill up this announcement post :slight_smile:

1 Like

I’m on it right now, but I’m struggling a little bit. I’m on firefox and this is what it looks like to me without Breeze Dark:

A tiny circle with a tinier check inside when it’s checked. I tried a new, fresh firefox profile and a different PC (also firefox) and get the same thing everywhere. I also investigated, and the ::before and ::after elements don’t get created at all, which is weird. Apparently setting display: block; on them could help, but I modified the script and still nothing. Any clues what’s going on?


Oh, I should have tested it with Firefox. I will try to fix this.

EDIT: Interesting… it seems that ::before and ::after are not supported for input elements. Chrome does it anyway, so I did not notice. And Firefox seems to also support it for <input type="radio"> – just <input type="checkbox"> does not work in Firefox.


I have fixed the problem – the ::before and ::after pseudo-elements are now in the label instead of in the input. The nice side-effect is that it’s now unaffected by Breeze Dark :slight_smile:

Furthermore, I have updated the positioning of the switch so that it does not overlap the gear of the new Extra Study Mover script.

I noticed this, but I thought that likely nobody wants to access the downloads during reviews anyway, so I did overwrite it intentionally. But if you think that overwriting browser behavior should be avoided regardless, I can change it to ALT + J in the next update.

Since the fetch-wrapping-method seems to work more reliable than I expected, I might do that. I just wanted to wait for feedback from the beta testers. :wink: However, I’m not sure if I should put them all in one thread, or create separate threads.


Hmm, it’s not overwriting it for me - just doing both. It plays the audio and opens the downloads! Could possibly be because I’m using Brave… I can also just change it locally if you’d rather keep it on CTRL, up to you!

I’m sure either will be fine!

Hopefully this helps me distinguish 上げる friom 上がる

self-study quiz in shambles


sigh of relief

@mods Are you aware that the extra study queue has these two very significant differences from the review queue?

  1. With the exception of the very first review, meaning and reading are back to back.
  2. The active queue is not a pool of active items

Both of these are due to this snippet of code which chooses the next item.

selectQuestion(postSubmitCompleteCallback) {
    const list = $.jStorage.get("activeQueue");
    const item = list[list.length - 1];

Instead of choosing a random item from the active queue, it always chooses the last item. This means that when you finish either the meaning or reading question and the script attempts to pick a new question, it picks the same question. Also, since the active queue is also replenished at the end, whenever it is replenished it the script will pick the new items before the old ones, such that the first items in the queue are not shown to the user until the very end of the session.

For what it is worth I did try it without any scripts running, but I looked at the code and it’s pretty clear what it is doing.