Wooo, it’s here!
Not sure if you were looking for some type of input but thanks for informing us about the project!
We don’t need any kind of input from mods, we’re just here to announce and recruit. But thank you for your response!
Thank you for this cool project, I’ve just tried it and it seems not many items are covered so far? What are the current stats for Kanji?
So far we’re at 91 submissions covering 27 items and another 37 half-done (0.71%), we’re just getting started. I’m not sure how many are kanji. We are importing the ones from the earlier thread and are thinking of importing AmandaBear’s art which will help a lot but we need to get in touch with her and make sure we have permission (and still sorting out how to do that). As we get more and more people contributing though I’m optimistic we’ll make faster progress.
Did you guys take pictures down from the original thread from the script? I remember a few of my pictures were used, but now they’re no longer there.
We had to reset when we rebuilt the scripts and moved the database, but we’re in the process of re-adding the ones from the original thread. Don’t worry, we definitely want to use all of those.
It’s incredible how quickly AI-generated art has progressed. I first remember seeing it roughly a year or so ago and the images were barely legible. Now I have to look closely to see the “computer prints” (probably a better pun there).
Anyway, super cool idea. Please feel free to post more notable examples!
I got access to Midjourney today and also joined the discord
Looking forward to adding some pictures. I tried the ai already and it’s amazing!
This is how Skynet was born.
These are hilarious!
@Sinyaven the script you made firs\t is not working any more:
Is related at the the script depandcy WK Item Info Injector
Could you check how to get this working again. I have checked the script topics but could not find your version.
@saraqael has based this script on my Mnemonic Artwork script. Comparing the two scripts, the following version should make it work with the new pages:
// ==UserScript==
// @name WaniKani AI Mnemonic Images
// @namespace aimnemonicimages
// @version 1.6
// @description Adds AI images to radical, kanji, and vocabulary mnemonics.
// @author Sinyaven (modified by saraqael)
// @license MIT-0
// @match https://www.wanikani.com/*
// @match https://preview.wanikani.com/*
// @require https://greasyfork.org/scripts/430565-wanikani-item-info-injector/code/WaniKani%20Item%20Info%20Injector.user.js?version=1166918
// @homepageURL https://community.wanikani.com/t/new-volunteer-project-were-using-ai-to-create-mnemonic-images-for-every-radical-kanji-vocabulary-come-join-us/58234
// @grant none
// ==/UserScript==
(async function () {
"use strict";
/* global wkItemInfo */
/* eslint no-multi-spaces: "off" */
//////////////
// settings //
//////////////
const ENABLE_RESIZE_BY_DRAGGING = true;
const USE_THUMBNAIL_FOR_REVIEWS = true;
const USE_THUMBNAIL_FOR_ITEMINF = false;
//////////////
if (!localStorage.getItem("AImnemonicMaxSize")) localStorage.setItem("AImnemonicMaxSize", 400); // standard size
const folderNames = {
r: 'Radicals',
k: 'Kanji',
v: 'Vocabulary',
}
function getUrl(wkId, type, mnemonic, thumb = false) {
return 'https://wk-mnemonic-images.b-cdn.net/' + type + '/' + mnemonic + '/' + wkId + (thumb ? '-thumb.jpg' : '.png');
}
function init() {
wkItemInfo.forType("radical,kanji,vocabulary").under("meaning").append("Meaning Mnemonic Image", ({ id, type, on }) => artworkSection(id, type, 'Meaning', on));
wkItemInfo.forType("radical,kanji,vocabulary").under("reading").append("Reading Mnemonic Image", ({ id, type, on }) => artworkSection(id, type, 'Reading', on));
}
async function artworkSection(subjectId, type, mnemonic, page) {
const fullType = folderNames[type[0].toLowerCase()];
const isItemInfo = page === 'itemPage';
const useThumbnail = isItemInfo ? USE_THUMBNAIL_FOR_ITEMINF : USE_THUMBNAIL_FOR_REVIEWS;
const imageUrl = getUrl(subjectId, fullType, mnemonic, useThumbnail); // get url (thumbnail in reviews and lessons)
const image = document.createElement("img"); // image loading
if (!(await new Promise(res => {
image.onload = () => res(true);
image.onerror = () => res(false);
image.src = imageUrl;
}))) return null;
if (ENABLE_RESIZE_BY_DRAGGING) {
const currentMax = parseInt(localStorage.getItem("AImnemonicMaxSize")) || 900;
makeMaxResizable(image, currentMax).afterResize(m => { localStorage.setItem("AImnemonicMaxSize", m); let e = new Event("storage"); e.key = "AImnemonicMaxSize"; e.newValue = m; dispatchEvent(e); });
addEventListener("storage", e => { if (e.key === "AImnemonicMaxSize") { image.style.maxWidth = `min(${e.newValue}px, 100%)`; image.style.maxHeight = e.newValue + "px"; } });
}
return image;
}
function makeMaxResizable(element, currentMax, lowerBound = 200) {
let size = 0;
let max = currentMax;
let oldMax = currentMax;
let callback = () => { };
let pointers = [{ id: NaN, x: 0, y: 0 }]; // image origin is always a pointer (scaling center)
function getDistanceSum(e) {
removePointer(e);
addPointer(e);
function length(p1, p2) { let d = [p1.x - p2.x, p1.y - p2.y]; return Math.sqrt(d[0] * d[0] + d[1] * d[1]); }
return pointers.reduce((total, p1) => pointers.reduce((l, p2) => l + length(p1, p2), total), 0);
//return pointers.reduce(([len, lastP], p) => [len + length(lastP, p), p], [0, pointers[pointers.length - 1]])[0]; // old version using circumference - order dependent! => not usable if pointers.length > 3
};
function removePointer(e) {
if (e) pointers = pointers.filter(p => p.id !== e.pointerId);
}
function addPointer(e) {
if (!e) return;
let rect = element.getBoundingClientRect();
pointers.push({ id: e.pointerId, x: e.clientX - rect.left, y: e.clientY - rect.top });
}
function startResizing(e) {
if (e.button !== 0) return;
if (pointers.length < 2) {
max = parseFloat(element.style.maxHeight);
oldMax = max;
}
size = getDistanceSum(e);
element.addEventListener("pointermove", doResizing);
element.addEventListener("pointerup", endResizing);
element.addEventListener("pointercancel", cancelResizing);
element.setPointerCapture(e.pointerId);
e.preventDefault();
}
function doResizing(e) {
if (!(e.buttons & 1)) return;
let newSize = getDistanceSum(e);
max *= newSize / size;
size = newSize;
updateMax();
};
function endResizing(e) {
doResizing(e);
max = Math.min(max, element.parentElement.clientWidth, element.naturalWidth);
oldMax = Math.max(max, lowerBound);
cancelResizing(e);
callback(max);
}
function cancelResizing(e) {
removePointer(e);
size = getDistanceSum();
if (pointers.length > 1) return;
max = oldMax;
updateMax();
element.removeEventListener("pointermove", doResizing);
element.removeEventListener("pointerup", endResizing);
element.removeEventListener("pointercancel", cancelResizing);
element.releasePointerCapture(e.pointerId);
}
function updateMax() {
let m = Math.max(max, lowerBound);
element.style.maxWidth = `min(${m}px, 100%)`;
element.style.maxHeight = m + "px";
};
updateMax();
element.style.touchAction = "pan-x pan-y";
element.addEventListener("pointerdown", startResizing);
return { afterResize: f => { callback = f; } };
}
init();
})();
Thank you for updating the script I forked from yours! You’re really fast with this. I’m going to update it on the download page as well.
Your own version works, but this new one does not.
Strange. Is it because the github key change perhaps?
For me, the code that I posted above works. If it doesn’t work for you, can you go through the steps in this guide and provide more info about what doesn’t work?
The problem lies in the fact that not every item has an image.
So your script works as stated by you.
Seems like this project might be a bit dead? Trying to make new submissions on the Discord server doesn’t seem to function anymore. Anyway, here’s a 魚 / さかな / soccer nun fish:
Hey gills, sorry for that! I got the server back online and you should be able to submit this awesome creation now. I also confirmed that the Tampermonkey JS script is working too, you can see it in action e.g. on this page: kanji/起
I also published a new version of the Tampermonkey script that makes it work properly on the new review pages (I only updated the list of matching URLs, but I tested it and it works fine).
Please let me know if you need any help with anything at all, you can ping me on the server, I am Chiara#9001!
Brilliant, thanks so much for all of this!