diff --git a/js/random.js b/js/random.js
index bd2d3220fbd94e86e9af0585e10546ba45383286..48a110f2bf95aae17796265eedf327d66a6638bd 100644
--- a/js/random.js
+++ b/js/random.js
@@ -1,6 +1,8 @@
 /**
  * generate two independent Gaussian numbers using Box-Muller transform.
  * mean and deviation specify the desired mean and standard deviation.
+ * @param {number} [mean]
+ * @param {number} [deviation]
  * @returns {number[]}
  */
 function gaussianPair(mean = 0, deviation = 1) {
@@ -9,9 +11,16 @@ function gaussianPair(mean = 0, deviation = 1) {
 	return [r * Math.cos(sigma), r * Math.sin(sigma)].map(val => val * deviation + mean);
 }
 
-// Generate a random integer with a normal distribution between min and max (both inclusive).
-// Default parameters result in truncating the standard normal distribution between -3 and +3.
-// Not specifying min/max results in rerolling val approximately 0.3% of the time.
+/**
+ * Generate a random integer with a normal distribution between min and max (both inclusive).
+ * Default parameters result in truncating the standard normal distribution between -3 and +3.
+ * Not specifying min/max results in rerolling val approximately 0.3% of the time.
+ * @param {number} [mean]
+ * @param {number} [deviation]
+ * @param {number} [min]
+ * @param {number} [max]
+ * @returns {number}
+ */
 function normalRandInt(mean = 0, deviation = 1, min = mean - 3 * deviation, max = mean + 3 * deviation) {
 	let val = gaussianPair(mean, deviation)[0];
 	while (val < min || val > max) {
@@ -25,6 +34,7 @@ function normalRandInt(mean = 0, deviation = 1, min = mean - 3 * deviation, max
  * If count is defined, chooses that many random numbers between min and max and returns the average. This is an approximation of a normal distribution.
  * @param {number} min
  * @param {number} max
+ * @param {number} [count]
  * @returns {number}
  */
 function jsRandom(min, max, count = 1) {
@@ -62,7 +72,7 @@ function jsRandomMany(arr, count) {
 /**
  * Accepts both an array and a list, returns undefined if nothing is passed.
  * @param {any[]} choices
- * @param {any[]} [otherChoices]
+ * @param {any} [otherChoices]
  * @returns {any}
  */
 function jsEither(choices, ...otherChoices) {
diff --git a/src/js/assayJS.js b/src/js/assayJS.js
index 8ba52b8e1a3ed98b56038ace4fc39fee6facba24..68a3ec9ed0ccd585dddf27bfd0ceaca48e6f66f4 100644
--- a/src/js/assayJS.js
+++ b/src/js/assayJS.js
@@ -272,7 +272,7 @@ window.removeSlave = function removeSlave(index) {
 
 /**
  * @param {App.Entity.SlaveState[]} [slaves]
- * @returns {Object.<number, number>}
+ * @returns {object.<number, number>}
  */
 window.slaves2indices = function slaves2indices(slaves = State.variables.slaves) {
 	return slaves.reduce((acc, slave, i) => { acc[slave.ID] = i; return acc; }, {});
@@ -681,7 +681,7 @@ window.PoliteRudeTitle = function PoliteRudeTitle(slave) {
  * @returns {string}
  */
 window.SlaveTitle = function SlaveTitle(slave) {
-	let r = "";
+	let r;
 	if (V.newDescriptions === 1) {
 		if (slave.dick > 0 && slave.balls > 0 && slave.boobs > 300 && slave.vagina > -1 && slave.ovaries === 1) {
 			if (jsRandom(1, 100) > 50) {
@@ -1832,4 +1832,4 @@ window.isShelterSlave = function isShelterSlave(slave) {
  */
 window.perceivedGender = function(slave) {
 	return -1;
-};
\ No newline at end of file
+};
diff --git a/src/js/utilsDOM.js b/src/js/utilsDOM.js
index 459914c0113ad9fc5852bdbc7af41fc8a0671063..80194e7e9d9b3bfb3b3d04e024472b679c914646 100644
--- a/src/js/utilsDOM.js
+++ b/src/js/utilsDOM.js
@@ -199,7 +199,7 @@ App.UI.DOM.combineNodes = function(...content) {
  * @param {boolean} [numberOnly]
  * @returns {HTMLInputElement}
  */
-App.UI.DOM.makeTextBox = function(defaultValue, onEnter, numberOnly = false, passage) {
+App.UI.DOM.makeTextBox = function(defaultValue, onEnter, numberOnly = false) {
 	const input = document.createElement("input");
 	input.type = "text";
 	input.value = defaultValue;
@@ -211,24 +211,20 @@ App.UI.DOM.makeTextBox = function(defaultValue, onEnter, numberOnly = false, pas
 		to 0 and trigger a change event we can't distinguish from setting the value to 0 explicitly.
 		The workaround is resetting the value to the last known valid value and not triggering onEnter.
 		*/
+		let oldValue = defaultValue;
 		updateValue = event => {
 			const newValue = Number(event.target.value);
 			if (!Number.isNaN(newValue)) {
 				onEnter(newValue);
-				event.target.oldValue = newValue;
+				oldValue = newValue;
 			} else {
 				// reset the value to the last known valid value
-				event.target.value = event.target.oldValue;
+				event.target.value = oldValue;
 			}
 		};
-		input.oldValue = defaultValue;
 	} else {
 		updateValue = e => {
 			onEnter(e.target.value);
-			if (passage !== '') {
-				SugarCube.Engine.play(passage);
-			}
-			console.log("passage", passage);
 		};
 	}
 	input.addEventListener('change', updateValue);
@@ -246,9 +242,7 @@ App.UI.DOM.colorInput = function(defaultValue, onEnter) {
 	input.type = "color";
 	input.value = defaultValue;
 
-	let updateValue = e => { onEnter(e.target.value); };
-
-	input.addEventListener('change', updateValue);
+	input.addEventListener('change', e => { onEnter(e.target.value); });
 
 	return input;
 };
diff --git a/src/js/utilsFC.js b/src/js/utilsFC.js
index b320b2b8b1867da9ac433c363e2f0744d44846d1..86717839ff1482f77b213d2dc68e2213b38b8d41 100644
--- a/src/js/utilsFC.js
+++ b/src/js/utilsFC.js
@@ -554,7 +554,6 @@ window.Height = (function() {
 		return table[`${nationality}.${race}`] || table[nationality] || table[`.${race}`] || table[""] || def;
 	};
 
-
 	/**
 	 * Helper method: Generate a skewed normal random variable with the skew s
 	 * Reference: http://azzalini.stat.unipd.it/SN/faq-r.html
@@ -608,8 +607,8 @@ window.Height = (function() {
 		if (!_.isFinite(age) || age < 2 || age >= 20) {
 			return height;
 		}
-		let minHeight = 0;
-		let midHeight = 0;
+		let minHeight;
+		let midHeight;
 		let midAge;
 		switch (genes) {
 			case "XX": // female
@@ -666,7 +665,7 @@ window.Height = (function() {
 			case "XY": // male
 				result = nationalityMeanHeight(xyMeanHeight, nationality, race);
 				break;
-				// special cases. Extra SHOX genes on X and Y chromosomes make for larger people
+			// special cases. Extra SHOX genes on X and Y chromosomes make for larger people
 			case "X0":
 			case "X": // Turner syndrome female
 				result = nationalityMeanHeight(xxMeanHeight, nationality, race) * 0.93;
@@ -1116,7 +1115,9 @@ window.numberWithPluralOne = function(number, single, plural) {
 };
 // shows "less than one (slave)" instead of "no (slaves)" when number is 0.
 window.numberWithPluralNonZero = function(number, single, plural) {
-	if (number === 0) { number = 0.1; }
+	if (number === 0) {
+		number = 0.1;
+	}
 	return numberWithPlural(number, single, plural);
 };
 window.onlyPlural = function(number, single, plural) {
@@ -1142,7 +1143,7 @@ window.commaNum = function(s) {
 	if (!s) {
 		return "0";
 	}
-	if (State.variables.formatNumbers !== 1) {
+	if (V.formatNumbers !== 1) {
 		return s.toString();
 	} else {
 		return s.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
@@ -1169,25 +1170,25 @@ window.years = function(weeks) {
 	years = Math.trunc(weeks / 52);
 
 	if (years >= 1) { // Is there at least 1 year
-		weeks = weeks-(years*52); // Find leftover weeks
+		weeks = weeks - (years * 52); // Find leftover weeks
 	}
-	if (weeks && weeks/13 >= 1) { // Is there at least 1 quarter
-		quarters = Math.trunc(weeks/13); // How many quarters?
-		weeks = weeks-(quarters*13); // A quarter contains 13 weeks, how many extra weeks do we have?
+	if (weeks && weeks / 13 >= 1) { // Is there at least 1 quarter
+		quarters = Math.trunc(weeks / 13); // How many quarters?
+		weeks = weeks - (quarters * 13); // A quarter contains 13 weeks, how many extra weeks do we have?
 	}
-	if (weeks && weeks/4 >= 1) { // Is there at least 1 month
-		months = Math.trunc(weeks/4); // How many months?
+	if (weeks && weeks / 4 >= 1) { // Is there at least 1 month
+		months = Math.trunc(weeks / 4); // How many months?
 		if (months === 3) { // Almost a quarter of a year
 			months--; // Quarters have 13 weeks though, so let's be sure the extra is in weeks.  Otherwise 51 will return "12 months" instead of "11 months and 4 weeks."
 		}
-		weeks = weeks-(months*4); // A month contains 4 weeks, how many extra weeks do we have?
+		weeks = weeks - (months * 4); // A month contains 4 weeks, how many extra weeks do we have?
 	}
 
 	// So we have years, quarters, months, and weeks.
 
 	// Quarters are useless so:
 
-	months += quarters*3; // Each quarter has three months.
+	months += quarters * 3; // Each quarter has three months.
 
 	if (years) {
 		array.push(`${num(years)} year${years !== 1 ? `s` : ``}`);
@@ -1201,13 +1202,7 @@ window.years = function(weeks) {
 		array.push(`${num(weeks)} week${weeks !== 1 ? `s` : ``}`);
 	}
 
-	r += array[0];
-	if (array.length === 3) {
-		r += `, ${array[1]} and ${array[2]}`;
-	} else if (array.length === 2) {
-		r += ` and ${array[1]}`;
-	}
-	return r;
+	return array.toStringExt();
 };
 /**
  * @param {number} [weeks]
@@ -1216,7 +1211,7 @@ window.years = function(weeks) {
  */
 window.asDate = function(weeks = null, bonusDay = 0) {
 	if (weeks == null) {
-		weeks = State.variables.week;
+		weeks = V.week;
 	}
 	let d = new Date(2037, 0, 12);
 	d.setDate(d.getDate() + weeks * 7 + bonusDay);
@@ -1243,15 +1238,15 @@ window.cashFormat = function(s) {
 };
 window.cashFormatColor = function(s, invert = false) {
 	if (invert) {
-		s = -1*s;
+		s = -1 * s;
 	}
 	// Display red if the value is negative, unless invert is true
 	if (s < 0) {
 		return `<span class='red'>${cashFormat(s)}</span>`;
-	// White for exactly zero
-	} else if (s === 0 ) {
+		// White for exactly zero
+	} else if (s === 0) {
 		return `<span>${cashFormat(s)}</span>`;
-	// Yellow for positive
+		// Yellow for positive
 	} else {
 		return `<span class='yellowgreen'>${cashFormat(s)}</span>`;
 	}
@@ -1447,115 +1442,134 @@ window.getSlaveTrustClass = function(slave) {
 
 /**
  * Takes an integer e.g. $activeSlave.hLength, returns a string in the format 10 inches
- * @param {number} s
+ * @param {number} cm
  * @returns {string}
  */
-window.cmToInchString = function(s) {
-	let inches = (Math.round(s / 2.54)).toString();
-	if (inches === "0") {
-		if (s === 0) {
-			inches += " inches";
-		} else {
-			inches = "less than an inch";
-		}
-	} else if (inches === "1") {
-		inches += " inch";
-	} else {
-		inches += " inches";
+window.cmToInchString = function(cm) {
+	let inches = cm / 2.54;
+	if (inches > 0 && inches < 1) {
+		return "less than an inch";
 	}
-	return inches;
+	inches = Math.round(inches);
+	if (inches === 1) {
+		return "1 inch";
+	}
+	return `${inches} inches`;
 };
 
 /**
  * takes an integer e.g. $activeSlave.height, returns a string in the format 6'5"
- * @param {number} s
+ * @param {number} cm
  * @returns {string}
  */
-window.cmToFootInchString = function(s) {
-	if (Math.round(s / 2.54) < 12) {
-		return cmToInchString(s);
+window.cmToFootInchString = function(cm) {
+	if (Math.round(cm / 2.54) < 12) {
+		return cmToInchString(cm);
 	}
-	return `${Math.trunc(Math.round(s/2.54)/12)}'${Math.round(s/2.54)%12}"`;
+	return `${Math.trunc(Math.round(cm / 2.54) / 12)}'${Math.round(cm / 2.54) % 12}"`;
 };
 
 /**
  * takes a dick value e.g. $activeSlave.dick, returns a string in the format 6 inches
- * @param {number} s
+ * @param {number} dick
  * @returns {string}
  */
-window.dickToInchString = function(s) {
-	return cmToInchString(dickToCM(s));
+window.dickToInchString = function(dick) {
+	return cmToInchString(dickToCM(dick));
 };
 
 /**
  * takes a dick value e.g. $activeSlave.dick, returns an int of the dick length in cm
- * @param {number} s
+ * @param {number} dick
  * @returns {number}
  */
-window.dickToCM = function(s) {
-	if (s < 9) {
-		return s * 5;
-	} else if (s === 9) {
+window.dickToCM = function(dick) {
+	if (dick < 9) {
+		return dick * 5;
+	} else if (dick === 9) {
 		return 50;
 	}
-	return s * 6;
+	return dick * 6;
 };
-
-// takes a ball value e.g. $activeSlave.balls, returns a string in the format 3 inches
-window.ballsToInchString = function(s) {
-	return cmToInchString(ballsToCM(s));
+/**
+ * takes a ball value e.g. $activeSlave.balls, returns a string in the format 3 inches
+ * @param {number} balls
+ * @returns {string}
+ */
+window.ballsToInchString = function(balls) {
+	return cmToInchString(ballsToCM(balls));
 };
 
-// takes a ball value e.g. $activeSlave.balls, returns an int of the ball size in cm
-window.ballsToCM = function(s) {
-	if (s < 2) {
+/**
+ * takes a ball value e.g. $activeSlave.balls, returns an int of the ball size in cm
+ * @param {number} balls
+ * @returns {number}
+ */
+window.ballsToCM = function(balls) {
+	if (balls < 2) {
 		return 0;
 	}
-	return (s < 10 ? (s - 1) * 2 : s * 2);
+	return (balls < 10 ? (balls - 1) * 2 : balls * 2);
 };
 
-// takes a dick value e.g. $activeSlave.dick, returns a string in the format of either `20cm (8 inches)`, `8 inches`, or `20cm`
-window.dickToEitherUnit = function(s) {
-	if (State.variables.showInches === 1) {
-		return `${dickToCM(s)}cm (${dickToInchString(s)})`;
+/**
+ * takes a dick value e.g. $activeSlave.dick, returns a string in the format of either `20cm (8 inches)`, `8 inches`, or `20cm`
+ * @param {number} dick
+ * @returns {string}
+ */
+window.dickToEitherUnit = function(dick) {
+	if (V.showInches === 1) {
+		return `${dickToCM(dick)}cm (${dickToInchString(dick)})`;
 	}
-	if (State.variables.showInches === 2) {
-		return dickToInchString(s);
+	if (V.showInches === 2) {
+		return dickToInchString(dick);
 	}
-	return `${dickToCM(s)}cm`;
+	return `${dickToCM(dick)}cm`;
 };
 
-// takes a ball value e.g. $activeSlave.balls, returns a string in the format of either `20cm (8 inches)`, `8 inches`, or `20cm`
-window.ballsToEitherUnit = function(s) {
-	if (State.variables.showInches === 1) {
-		return `${ballsToCM(s)}cm (${ballsToInchString(s)})`;
+/**
+ * takes a ball value e.g. $activeSlave.balls, returns a string in the format of either `20cm (8 inches)`, `8 inches`, or `20cm`
+ * @param {number} balls
+ * @returns {string}
+ */
+window.ballsToEitherUnit = function(balls) {
+	if (V.showInches === 1) {
+		return `${ballsToCM(balls)}cm (${ballsToInchString(balls)})`;
 	}
-	if (State.variables.showInches === 2) {
-		return ballsToInchString(s);
+	if (V.showInches === 2) {
+		return ballsToInchString(balls);
 	}
-	return `${ballsToCM(s)}cm`;
+	return `${ballsToCM(balls)}cm`;
 };
 
-// takes an int in centimeters e.g. $activeSlave.height, returns a string in the format of either `200cm (6'7")`, `6'7"`, or `200cm`
-window.heightToEitherUnit = function(s) {
-	if (State.variables.showInches === 1) {
-		return `${s}cm (${cmToFootInchString(s)})`;
+/**
+ * takes an int in centimeters e.g. $activeSlave.height, returns a string in the format of either `200cm (6'7")`, `6'7"`, or `200cm`
+ * @param {number} height
+ * @returns {string}
+ */
+window.heightToEitherUnit = function(height) {
+	if (V.showInches === 1) {
+		return `${height}cm (${cmToFootInchString(height)})`;
 	}
-	if (State.variables.showInches === 2) {
-		return cmToFootInchString(s);
+	if (V.showInches === 2) {
+		return cmToFootInchString(height);
 	}
-	return `${s}cm`;
+	return `${height}cm`;
 };
 
-// takes an int in centimeters e.g. $activeSlave.hLength, returns a string in the format of either `30cm (12 inches)`, `12 inches`, or `30cm`
-window.lengthToEitherUnit = function(s) {
-	if (State.variables.showInches === 1) {
-		return `${s}cm (${cmToInchString(s)})`;
+/**
+ * takes an int in centimeters e.g. $activeSlave.hLength, returns a string in the format of either `30cm (12 inches)`, `12 inches`, or `30cm`
+ * @param {number} length
+ * @returns {string}
+ */
+window.lengthToEitherUnit = function(length) {
+	if (V.showInches === 1) {
+		return `${length}cm (${cmToInchString(length)})`;
 	}
-	if (State.variables.showInches === 2) {
-		return cmToInchString(s);
+	if (V.showInches === 2) {
+		return cmToInchString(length);
 	}
-	return `${s}cm`;
+	return `${length}cm`;
 };
 
 /**
@@ -1575,6 +1589,7 @@ window.induceLactation = function induceLactation(slave) {
 	}
 	return r;
 };
+
 window.getProstheticsStockpile = function() {
 	return `<div>Prosthetics interfaces: ${num(V.prosthetics.interfaceP1.amount + V.prosthetics.interfaceP2.amount)}</div>` +
 		`<div class="choices">Basic: ${V.prosthetics.interfaceP1.amount}</div>` +
@@ -2201,6 +2216,7 @@ window.SkillIncrease = (function() {
 		slave.skill.oral += skillIncrease;
 		return r;
 	}
+
 	/* call as SkillIncrease.Vaginal() */
 	/**
 	 * @param {App.Entity.SlaveState} slave
@@ -2328,7 +2344,7 @@ window.SkillIncrease = (function() {
 
 window.upgradeMultiplier = function(skill) {
 	if (skill === 'medicine' && V.PC.career === "medicine" || skill === 'engineering' && V.PC.career === "engineer"
-	|| ((skill === 'medicine' || skill === 'engineering') && V.arcologies[0].FSRestartDecoration >= 100 && V.eugenicsFullControl === 0)) {
+		|| ((skill === 'medicine' || skill === 'engineering') && V.arcologies[0].FSRestartDecoration >= 100 && V.eugenicsFullControl === 0)) {
 		return 0.6;
 	}
 	if (V.PC.skill[skill] <= -100) {
@@ -2612,8 +2628,12 @@ window.changeSkinTone = function(skin, value) {
 	}
 	let prop;
 	for (prop in skinToMelanin) {
-		if (!skinToMelanin.hasOwnProperty(prop)) { continue; }
-		if (newSkin >= skinToMelanin[prop]) { return prop; }
+		if (!skinToMelanin.hasOwnProperty(prop)) {
+			continue;
+		}
+		if (newSkin >= skinToMelanin[prop]) {
+			return prop;
+		}
 	}
 	return prop;
 };
@@ -2646,7 +2666,6 @@ window.nippleColorLevel = function(color) {
  * @returns {string}
  */
 
-
 /**
  * Expression for SugarCube for referencing a slave by index
  * @param {number} i slave array index or -1 for activeSlave
@@ -2662,7 +2681,7 @@ App.Utils.slaveRefString = function(i) {
  * @returns {App.Entity.SlaveState}
  */
 App.Utils.slaveByIndex = function(i) {
-	return i === -1 ? State.variables.activeSlave : State.variables.slaves[i];
+	return i === -1 ? V.activeSlave : V.slaves[i];
 };
 
 /**
@@ -2671,7 +2690,7 @@ App.Utils.slaveByIndex = function(i) {
  */
 App.Utils.setActiveSlaveByIndex = function(index) {
 	if (index >= 0) {
-		State.variables.activeSlave = State.variables.slaves[index];
+		V.activeSlave = V.slaves[index];
 	}
 };
 
@@ -2681,7 +2700,7 @@ App.Utils.setActiveSlaveByIndex = function(index) {
  * @returns {number}
  */
 App.Utils.slaveIndexForId = function(id) {
-	return State.variables.slaveIndices[id];
+	return V.slaveIndices[id];
 };
 
 /**
@@ -2710,7 +2729,7 @@ App.Utils.setLocalPronouns = function(slave, suffix, pronouns) {
 		'Woman', 'Women', 'Loli', 'Daughter', 'Sister', 'Wife', 'Wives', 'Mother', 'Mothers'
 	]; // Pronouns always refer to the slave in question, never any relation of theirs.  It is "mother" as in "she is a mother of many" not "you are her mother".  Plural pronouns would refer to "wives like her," not "her wives."
 
-	const scope = pSuffix.length === 0 ? State.variables : State.temporary;
+	const scope = pSuffix.length === 0 ? V : State.temporary;
 	pronouns.forEach(p => {
 		scope[p + pSuffix] = ps[p];
 	});
@@ -2778,8 +2797,8 @@ window.disobedience = function(slave) {
 	}
 
 	// factors are between 0 (right on the boundary of perfectly obedient) and 10 (completely disobedient)
-	let devotionFactor = 10 - ((10 * (slave.devotion + 100))/(devotionBaseline + 100));
-	let trustFactor = (10 * (slave.trust - trustBaseline))/(100 - trustBaseline);
+	let devotionFactor = 10 - ((10 * (slave.devotion + 100)) / (devotionBaseline + 100));
+	let trustFactor = (10 * (slave.trust - trustBaseline)) / (100 - trustBaseline);
 	return Math.round(devotionFactor * trustFactor);
 };
 
@@ -2856,7 +2875,7 @@ window.generateSlaveID = function() {
 	// household liquidators and recETS generate slaves at an offset of 1000 (and many such slaves already exist)
 	// if you go through enough slaves you WILL generate collisions, so make sure we haven't just done that.
 	let allSlaveIDs = [...V.slaves.map((s) => s.ID), ...V.tanks.map((s) => s.ID), ...V.cribs.map((s) => s.ID)];
-	while (allSlaveIDs.contains(V.IDNumber)) {
+	while (allSlaveIDs.includes(V.IDNumber)) {
 		V.IDNumber++;
 	}
 	return V.IDNumber++;