[Userscript] Vertical Reviews

Not right now, to make it work well I’d need to re-organise what the page looks like (I imagine it would need a vertical column). That would make things more difficult to display and to find a way to not cause people eye strain as things randomly move around the page.

If anyone wants to have a go at this, feel free to have a go, and share it. the code is free for you to edit.

Maybe if you set it so stuff doesn’t move around the page once the script is enabled. That is to say, if script is on, the probability of vertical text and layout is 1. So people can use it to get used to vertical text and the layout is your version of an adapted layout so it works with all items.


Due to popular demand, I’ve updated the script. It now has options where you can switch between the current mode (randomly showing short words vertically) to an always-on mode where all words show vertically. Or you can keep it so just short words always show vertically, and not change the UI too much.

Do let me know if you find this update useful, or if things need tweaking.


still limited to 3 characters?

It has an options drop down at the bottom of the page, next to the little buttons. If you set it to “always”, then there is no character limit.

1 Like

sometimes it shows like this, any idea how to fix?

Do you run any other userscripts?

yes, I have others like advanced context sentence, that shows sentences during reviews and jitai, as you see in the picture, for handwriting.

For me, the vertical review items looked like this, they were squashed together against the top of the window. I’m not sure if it has something to do with another userscript i’m using (just Wanikani Override and Lightning Mode) or if it’s only happening in Firefox or on Mac (which i’m using).

I fixed this by adding a min-height to the last css definition in the script:

body:not(.lonm-alwaysvertical) #character.vocabulary.vertical {
   min-height: 35vh;

Edit: Thanks for the script btw, it’s a good idea!

1 Like

noticed this script hasn’t been working … not a programmer (don’t know if it’s other scripts or wk updates) just pointing it out

It’s working for me. Here’s some info that could help me diagnose it:

  • Your web browser (+version)
  • how you install the script
  • any other scripts you use
  • any browser extensions you use
  • if you right click > “inspect” while on the reviews page > go to console > any red messages

If you give me that info I can take a look at it when I get a free moment

thought it was something to do with the wk update…another script had broken and was updated… I’ll test on this end more…

hi, great script, always helpful!

could you update it to show in vertical mode in the new extra study mode?

Good idea. I’ve updated the script for this. v2.0.1 now supports extra study mode.


Never seen this script before! Great idea & can’t believe I never thought of that…

@lon this script is great, but the “vertical for short words” and “vertical for random short words” settings don’t seem to work with Reorder Omega enabled (only “always show vertically” works) - might it be an easy fix by you or Kumirei?
If not, no worries! :slight_smile:

Also @Kumirei, when this script is set to “always show vertically”, the Omega “Preset” text gets goes vertical as well, might that have a quick fix?


I’ll have a look at it tomorrow maybe


I imagine the text going vertical is because I apply a style rule tat everything in that part of the webpage needs to go vertical. That’s probably easy to fix.

I would need to investigate why it’s not working with the other options for short words. I don’t use that script, so I can’t guess what would be causing it. Maybe if the script is adding extra words, like I see in the screenshot, I’m counting the length of the word wrong so it never finds a “short” one.

1 Like

@Kumirei sorry for tagging you about this, all fixed so you don’t need to check anything in the end!

@lon Yep, you were right!
I modified the text-orientation CSS to only apply to the span inside the character id instead of anything in it so that it’ll only make the actual word vertical, and also changed the short() function to only take into account letters in that span. I also made it always take up 3 letters of height if vertical so that it’ll jump around a bit less!

Here’s the modified code just in case you want to update the script at some point (I don’t think I’ve broken anything, all seems to still be working :slight_smile:)

Modified code
// ==UserScript==
// @name        WaniKani Vertical
// @version     2.0.2
// @description Display WaniKani reviews with vertical text
// @author      @lonmcgregor
// @copyright   2021, lonm
// @license     MIT
// @namespace   lonm
// @homepageURL https://gist.github.com/LonMcGregor/972f523e601661475fd0600843f8fb55
// @include     https://www.wanikani.com/review/session
// @include     https://www.wanikani.com/extra_study/session*
// ==/UserScript==

function showControls(){
    // Choose between modes
    // Always = show all words vertically
    // Short = Show all short words vertically
    // Random = For short words, offer a 1:2 chance of showing them vertically
    const selector = document.createElement("select");
    selector.id = "lonm-verticaltextmod";
    selector.innerHTML = `<optgroup label="Controls for Vertical Text">
    <option value="always">Always show all reviews vertically</option>
    <option value="short">Show short reviews vertically</option>
    <option value="random">Show random short reviews vertically</option>
    selector.value = getSavedMode();
    selector.addEventListener("input", setSavedMode);

function getSavedMode(){
    // get the saved value. if its not set, default to random
    const storedvalue = localStorage.getItem("lonm-verticaltextmod");
    return storedvalue ? storedvalue : "random";

function setSavedMode(){
    // save the mode and update the page
    const storedvalue = document.querySelector("#lonm-verticaltextmod").value;
    localStorage.setItem("lonm-verticaltextmod", storedvalue);
    if(storedvalue === "always"){
    } else {

function addCss(){
    // Add custom css to the page to support the mod
    const alwaysvertical = `/* for the main page */
    .lonm-alwaysvertical #character {
        display: flex;
        align-items: center;
        justify-content: center;
        z-index: 1;
        float: right;
        width: 400px;
        height: 100vh !important;
    .lonm-alwaysvertical #character span {
        writing-mode: vertical-rl;
        text-orientation: upright;

    .lonm-alwaysvertical #information {
        width: calc(100% - 460px);

    .lonm-alwaysvertical #additional-content ul li {
        width: 15%;

    /* for the settings box */
    .lonm-alwaysvertical #reviews footer {
        position: fixed;
        bottom: 0;
        right: unset;
        left: 10px;
        z-index: 0;

    .lonm-alwaysvertical #reviews footer {
        position: fixed;
        bottom: 0;
        right: unset;
        left: 10px;
        z-index: 0;

    footer #lonm-verticaltextmod {
        background-color: #e1e1e1;
        border: none;
        color: #888888;
        width: 40px;
        height: 33px;
        margin-left: 10px;
        border-radius: 3px 3px 0 0;

    footer #lonm-verticaltextmod:focus {
        background-color: unset;
        color: unset;

    /* for random chances */
    body:not(.lonm-alwaysvertical) #character.vocabulary.vertical span {
        writing-mode: vertical-rl;
        text-orientation: upright;
        width: 100%;
        height: 3em;
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 12px 0 10px 0;

	/* for extra reviews, to make the title not overlap */
	div#menu-bar-title {
		text-align: left;
		padding-left: 60px;
    const style = document.createElement("style");
    const tn = document.createTextNode(alwaysvertical);

function short(){
    // 3 characters or shorter word
    return document.querySelector("#character").querySelector('span').innerText.length <= 3;

function random(){
    // either we always want it true for short words, or a 1:2 random chance
    return getSavedMode() === "short" | Math.random() <= 0.5;

function chooseOrientation(){
    // When not always on, handle choosing whether to show it or not
    if(short() && random()){
    } else {

// Observe when going to next review item
const VerticalTextMutationObserver = new MutationObserver(chooseOrientation);

function init(){
    if(document.querySelector("#loading") && document.querySelector("#loading").style.display === 'none'){
        VerticalTextMutationObserver.observe(document.querySelector("#character").querySelector('span'), {childList: true, subtree: true, characterData: true});
    } else {
        setTimeout(init, 150);

setTimeout(init, 500);

Thanks for the help!

1 Like

Thanks. Fixes look good, so I merged them into the main script.