Skip to content

Instantly share code, notes, and snippets.

Last active August 23, 2023 09:47
Show Gist options
  • Save obskyr/9f3c77cf6bf663792c6e to your computer and use it in GitHub Desktop.
Save obskyr/9f3c77cf6bf663792c6e to your computer and use it in GitHub Desktop.
Jitai (字体): A fairly full-featured font randomizer for WaniKani.

List of supported fonts and download links

Fonts at some point found on

(But, obscure Japanese links have a tendency of breaking, and a lot of these have moved. If you see a broken link, please tell me in the forum thread!)

I would recommend taking a look at these fonts, and installing the ones you'd like. Once installed, Jitai will automatically use them. I, personally, have them all installed - it's a good mix!

Jitai will only use any given font for words where it has all the kanji in it, so don't be afraid of installing fonts that only contain a limited subset of kanji - it can't hurt. Go wild!

Default Windows fonts

  • Meiryo
  • MS Gothic
  • MS Mincho
  • Yu Gothic
  • Yu Mincho

Default OS X fonts

  • Hiragino Kaku Gothic Pro
  • Hiragino Maru Gothic Pro
  • Hiragino Mincho Pro

Common Linux fonts

EPSON families (download and run the .exe)

  • EPSON 行書体M
  • EPSON 正楷書体M
  • EPSON 教科書体M
  • EPSON 太明朝体B
  • EPSON 太行書体B
  • EPSON 丸ゴシック体M

Other fonts (Typekit, etc.)

// ==UserScript==
// @name Jitai
// @version 1.3.2
// @description Display WaniKani reviews in randomized fonts, for more varied reading training.
// @author Samuel (@obskyr)
// @copyright 2016-2018, obskyr
// @license MIT
// @namespace
// @homepageURL
// @icon
// @include /^https?://(www\.)?wanikani\.com/review/session/?$/
// @grant none
// ==/UserScript==
To control which fonts to choose from, edit this list.
If you feel too many fonts of a certain type are showing
up, remove a few of those from the list. If you've got
fonts that aren't in the list that you'd like to be used,
add their names and they'll be in the rotation.
var fonts = [
// Default Windows fonts
"Meiryo, メイリオ",
"MS PGothic, MS Pゴシック, MS Gothic, MS ゴック",
"MS PMincho, MS P明朝, MS Mincho, MS 明朝",
"Yu Gothic, YuGothic",
"Yu Mincho, YuMincho",
// Default OS X fonts
"Hiragino Kaku Gothic Pro, ヒラギノ角ゴ Pro W3",
"Hiragino Maru Gothic Pro, ヒラギノ丸ゴ Pro W3",
"Hiragino Mincho Pro, ヒラギノ明朝 Pro W3",
// Common Linux fonts
"Takao Gothic, TakaoGothic",
"Takao Mincho, TakaoMincho",
"Sazanami Gothic",
"Sazanami Mincho",
"Kochi Gothic",
"Kochi Mincho",
"Dejima Mincho",
"Ume Gothic",
"Ume Mincho",
// Other Japanese fonts people use.
// You might want to try some of these!
"EPSON 行書体M",
"EPSON 正楷書体M",
"EPSON 教科書体M",
"EPSON 太明朝体B",
"EPSON 太行書体B",
"EPSON 丸ゴシック体M",
"A-OTF Shin Maru Go Pro",
"'chifont+', chifont",
"darts font",
"ArmedBanana", // This one is completely absurd. I recommend it.
"aoyagireisyosimo2, AoyagiKouzanFont2OTF",
// Add your fonts here!
"Fake font name that you can change",
"Another fake font name",
"Just add them like this!",
"Quotes around the name, comma after."
var existingFonts = [];
for (var i = 0; i < fonts.length; i++) {
var fontName = fonts[i];
if (fontExists(fontName)) {
function fontExists(fontName) {
// Approach from - thanks!
// Will return false for the browser's default monospace font, sadly.
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
var text = "wim-—l~ツ亻".repeat(100); // Characters with widths that often vary between fonts.
context.font = "72px monospace";
var defaultWidth = context.measureText(text).width;
// Microsoft Edge raises an error when a context's font is set to a string
// containing certain special characters... so that needs to be handled.
try {
context.font = "72px " + fontName + ", monospace";
} catch (e) {
return false;
var testWidth = context.measureText(text).width;
return testWidth != defaultWidth;
function canRepresentGlyphs(fontName, glyphs) {
var canvas = document.createElement('canvas');
canvas.width = 50;
canvas.height = 50;
var context = canvas.getContext("2d");
context.textBaseline = 'top';
var blank = document.createElement('canvas');
blank.width = canvas.width;
blank.height = canvas.height;
var blankDataUrl = blank.toDataURL();
context.font = "24px " + fontName;
var result = true;
for (var i = 0; i < glyphs.length; i++) {
context.fillText(glyphs[i], 0, 0);
if (canvas.toDataURL() === blankDataUrl) {
result = false;
context.clearRect(0, 0, canvas.width, canvas.height);
return result;
var jitai = {
setToRandomFont: function(glyphs) {
// The font is set as a randomly shuffled list of the existing fonts
// in order to always show a random font, even if the first one chosen
// doesn't have a certain glyph being attempted to be displayed.
var randomlyOrdered = this.getShuffledFonts();
// Some fonts don't contain certain radicals, for example, so it's best
// to check that the font used can represent all the glyphs. The reason
// the browser can't switch automatically is that some fonts report that
// they have a glyph, when in fact they just show up blank.
var currentFont;
if (glyphs) {
for (var i = 0; i < randomlyOrdered.length; i++) {
var fontName = randomlyOrdered[i];
if (canRepresentGlyphs(fontName, glyphs)) {
currentFont = fontName;
} else {
currentFont = randomlyOrdered.join(', ');
this.currentFont = currentFont;
this.$characterSpan.css('font-family', currentFont);
setToDefaultFont: function() {
this.$characterSpan.css('font-family', '');
setHoverFont: function(fontName) {
this.$hoverStyle.text("#character span:hover {font-family: " + fontName + " !important;}");
getShuffledFonts: function() {
// This shouldn't have to be part of the Jitai object,
// but it uses Jitai's local copy of Math.random, so
// this is pretty much the most reasonable way to do it.
var fonts = existingFonts.slice();
for (var i = fonts.length; i > 0;) {
var otherIndex = Math.floor(this.random() * i);
var temp = fonts[i];
fonts[i] = fonts[otherIndex];
fonts[otherIndex] = temp;
return fonts;
init: function() {
// Reorder scripts seem to like overwriting Math.random(!?), so this
// workaround is required for Jitai to work in conjunction with them.
var iframe = document.createElement('iframe');
iframe.className = 'jitai-workaround-for-reorder-script-compatibility'; = 'none';
this.random = iframe.contentWindow.Math.random;
this.$characterSpan = $('#character span');
this.defaultFont = this.$characterSpan.css('font-family');
this.$hoverStyle = $('<style/>', {'type': 'text/css'});
// answerChecker.evaluate is only called when checking the answer, which
// is why we catch it, check for the "proceed to correct/incorrect display"
// condition, and set the font back to default if it's a non-stopping answer.
var oldEvaluate = answerChecker.evaluate;
answerChecker.evaluate = function(questionType, answer) {
var result = oldEvaluate.apply(this, [questionType, answer]);
if (!result.exception) {
return result;
// $.jStorage.set('currentItem') is only called right when switching to a
// new question, which is why we hook into it to randomize the font at the
// exact right time: when a new item shows up.
var oldSet = $.jStorage.set;
$.jStorage.set = function(key, value, options) {
var ret = oldSet.apply(this, [key, value, options]);
if (key === 'currentItem') {
jitai.setToRandomFont(value.kan || value.voc || value.rad);
return ret;
$(document).ready(function() {
// Make sure page doesn't jump around on hover.
var $heightStyle = $('<style/>', {'type': 'text/css'});
var heightCss = "";
// The different heights here are equal to the different line-heights.
heightCss += "#question #character {height: 1.6em;}";
heightCss += "#question #character.vocabulary {height: 3.21em;}";
heightCss += "@media (max-width: 767px) {";
heightCss += " #question #character {height: 2.4em;}";
heightCss += " #question #character.vocabulary {height: 4.85em;}";
heightCss += "}";
Copy link

logic commented May 7, 2019

Would love to see the name of the font (maybe small and unobtrusive, in light text, somewhere off in a corner) when an alternate font has been selected. Some of the fonts you mentioned in the readme are...wild. :)

But regardless: this script has been invaluable for training my eyes to see the different strokes of a character rather than a specific font's idea what it should look like; thank you so much for making this available!

Copy link

ilyakamens commented Dec 29, 2019

FYI, on OS X 10.15.1, for Chihaya Gothic, chifont+, and Darts Font, I had to run the following command in this comment in a terminal to unzip the file: CocoaPods/CocoaPods#7711 (comment)

Double-clicking the zip file resulted in error 22, and using unzip in a terminal resulted in invalid byte sequence.

Copy link

Thanks for the curation of the fonts in addition to the

The name of the darts font (linked above) is DartsFont but it is listed as "darts font" in the script's default font list.

Easy to fix but someone might not notice if they install the entire list. :)

Copy link

Is anyone else using Ubuntu ? I'm on 20.04 and having some trouble with the EPSON font names. They don't display well like they use the shiftjis encoding

Copy link

AnOrinaryCabbage commented Apr 28, 2023

so after WK's new update, several things have updated (including the review page url) so if you can update the script i'd greatly appreciate it

Copy link

Ooooh boy, I really hate that it doesn't work anymore.

Apart from the URL not matching correctly (thanks for breaking all userscripts for no reason Wanikani "engineers"), there is no jQuery anymore since some rewrite they've done. I sure hope troubling the community with all this has been helpful at least for them, sigh...

Let me be helpful though, in case someone ends up here:
There is an updated version of this userscript that works, as of now.
Apparently, it has been abandoned by the original creator, so someone took up the job. I guess it's inevitable when they mess with things the community has made. Oh webdev.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment