Ipmplement woo generator

This commit is contained in:
2026-02-03 17:31:25 -06:00
parent c0def2f0a3
commit 8434a9dae5
5 changed files with 825 additions and 0 deletions

View File

@@ -0,0 +1,230 @@
---
layout: layouts/base.njk
title: "Woo-Woo Incense Description Generator"
structuredData: none
---
<div class="container">
<h1>Woo-Woo Incense Description Generator</h1>
<p>Ever wonder how people come up with those confusing esoteric descriptions of their incense complete with “medicinal effects?” Want some ideas to spice up your Instagram profile, website, or Etsy listings? Well youre in luck! Click the button below to generate an incense description so mystical youll swear your chakras are aligned! Music by <a href="https://pixabay.com/users/saavane-32312792/">saavane on Pixabay</a>. Woo sound effect by <a href="https://freesound.org/s/30995/">UncleSigmund on freesound</a>. Background effect from <a href="https://codepen.io/tommyho/pen/JjgoZLK">Tommy Ho on Codepen</a>. No <a href="/ai">AI</a> used—I can write slop on my own!</p>
<script src="/js/incense-description-generator.js"></script>
<script>
const audioOnClick = new Audio("/audio/30995__unclesigmund__woo-2.mp3");
const bgMusic = new Audio("/audio/new-sun-428916.mp3");
window.addEventListener("pointermove", (e) => {
bgMusic.play();
});
window.addEventListener("touchstart", (e) => {
bgMusic.play();
});
const generateWoo = (elementId, button) => {
const element = document.getElementById(elementId);
const wooButton = document.getElementById(button);
audioOnClick.play();
element.innerHTML = `
<div>
${descriptionConstructor(adjective, noun, bodyPart, verb, preposition, ingredient)}
</div>
`;
wooButton.innerHTML = 'Generate Some More Woo!';
}
</script>
<button onclick="generateWoo('wooContainer', 'wooButton')" id="wooButton">Generate Some Woo!</button>
<div class="card" id="wooContainer" style="padding: var(--space-s); margin-top: var(--space-l);">Click the button to generate woo…</div>
</div>
<!-- Background effect from https://codepen.io/tommyho/pen/JjgoZLK -->
<canvas id="shaderCanvas"></canvas>
<script src="/js/three.min.js"></script>
<script>
let scene, camera, renderer, uniforms, material, mesh;
function init() {
scene = new THREE.Scene();
camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('shaderCanvas'), antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
const vertexShader = `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = vec4(position, 1.0);
}
`;
const fragmentShader = `
uniform float time;
uniform vec2 resolution;
varying vec2 vUv;
#define PI 3.14159265358979323846
vec2 rotate(vec2 v, float a) {
float s = sin(a);
float c = cos(a);
mat2 m = mat2(c, -s, s, c);
return m * v;
}
float random(vec2 st) {
return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
}
float noise(vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
float a = random(i);
float b = random(i + vec2(1.0, 0.0));
float c = random(i + vec2(0.0, 1.0));
float d = random(i + vec2(1.0, 1.0));
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
}
float fbm(vec2 st) {
float value = 0.0;
float amplitude = 0.5;
float frequency = 0.0;
for (int i = 0; i < 6; i++) {
value += amplitude * noise(st);
st *= 2.0;
amplitude *= 0.5;
}
return value;
}
vec3 palette(float t, vec3 a, vec3 b, vec3 c, vec3 d) {
return a + b * cos(6.28318 * (c * t + d));
}
vec3 vibrancePalette(float t) {
vec3 a = vec3(0.5, 0.5, 0.5);
vec3 b = vec3(0.5, 0.5, 0.5);
vec3 c = vec3(1.0, 1.0, 1.0);
vec3 d = vec3(0.0, 0.33, 0.67);
return palette(t, a, b, c, d);
}
vec3 warmPalette(float t) {
vec3 a = vec3(0.5, 0.5, 0.5);
vec3 b = vec3(0.5, 0.5, 0.5);
vec3 c = vec3(1.0, 1.0, 1.0);
vec3 d = vec3(0.0, 0.10, 0.20);
return palette(t, a, b, c, d);
}
vec3 coolPalette(float t) {
vec3 a = vec3(0.5, 0.5, 0.5);
vec3 b = vec3(0.5, 0.5, 0.5);
vec3 c = vec3(1.0, 1.0, 1.0);
vec3 d = vec3(0.3, 0.20, 0.20);
return palette(t, a, b, c, d);
}
vec3 rainbowPalette(float t) {
vec3 a = vec3(0.5, 0.5, 0.5);
vec3 b = vec3(0.5, 0.5, 0.5);
vec3 c = vec3(1.0, 1.0, 1.0);
vec3 d = vec3(0.0, 0.33, 0.67);
return palette(t, a, b, c, d);
}
void main() {
vec2 st = gl_FragCoord.xy / resolution.xy;
st.x *= resolution.x / resolution.y;
vec2 q = vec2(0.);
q.x = fbm(st + 0.1 * time);
q.y = fbm(st + vec2(1.0));
vec2 r = vec2(0.);
r.x = fbm(st + 1.0 * q + vec2(1.7, 9.2) + 0.15 * time);
r.y = fbm(st + 1.0 * q + vec2(8.3, 2.8) + 0.126 * time);
float f = fbm(st + r);
vec2 p = st * 2.0 - 1.0;
float a = atan(p.y, p.x);
float r2 = length(p);
vec2 uv = vec2(a / PI, r2);
uv = rotate(uv, time * 0.1);
vec3 color1 = vibrancePalette(f + time * 0.1);
vec3 color2 = warmPalette(length(q));
vec3 color3 = coolPalette(length(r.x));
vec3 color4 = rainbowPalette(f * 2.0 + time * 0.2);
vec3 color = mix(color1, color2, 0.5);
color = mix(color, color3, 0.3);
color = mix(color, color4, sin(time * 0.1) * 0.5 + 0.5);
color += 0.05 * vec3(1.0) * smoothstep(0.1, 0.2, fbm(10.0 * uv + time * 0.5));
// Add some extra vibrancy
color = pow(color, vec3(0.8));
color *= 1.1;
gl_FragColor = vec4(color, 1.0);
}
`;
uniforms = {
time: { value: 1.0 },
resolution: { value: new THREE.Vector2() }
};
material = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: vertexShader,
fragmentShader: fragmentShader
});
mesh = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), material);
scene.add(mesh);
onWindowResize();
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize() {
renderer.setSize(window.innerWidth, window.innerHeight);
uniforms.resolution.value.x = renderer.domElement.width;
uniforms.resolution.value.y = renderer.domElement.height;
}
function animate(timestamp) {
requestAnimationFrame(animate);
uniforms.time.value = timestamp * 0.001;
renderer.render(scene, camera);
}
init();
animate(0);
</script>
<style>
body {
background: none;
}
canvas {
display: block;
opacity: .35;
pointer-events: none;
position: fixed;
top: 0;
left: 0;
z-index: -1;
}
}
</style>

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,589 @@
// Woo-woo incense description generator
const adjective = [
"aromatic",
"climatic",
"cooling",
"deep",
"dulled",
"effervescent",
"effluent",
"effulgent",
"endless",
"evocative",
"grounding",
"healing",
"hidden",
"indolent",
"juicy",
"lactescent",
"lingering",
"ministerial",
"nourishing",
"open",
"precious",
"pungent",
"resonant",
"restless",
"sacred",
"sensual",
"shimmering",
"sparkling",
"still",
"undulating",
"unparalleled",
"warming",
];
const noun = [
["astrology", "astrologies"],
["bloom", "blooms"],
["breath", "breaths"],
["chakra", "chakras"],
["clarity", "clarities"],
["contribution", "contributions"],
["curiosity", "curiosities"],
["direction", "directions"],
["ember", "embers"],
["connection", "connections"],
["constriction", "constrictions"],
["coolness", "coolnesses"],
["expression", "expressions"],
["fire", "fires"],
["geometry", "geometries"],
["glow", "glows"],
["harmony", "harmonies"],
["meaning", "meanings"],
["nourishment", "nourishments"],
["onanism", "onanisms"],
["relationship", "relationships"],
["resonance", "resonances"],
["ritual", "rituals"],
["smoke", "smokes"],
["space", "spaces"],
["surface", "surfaces"],
["tantra", "tantras"],
["tension", "tensions"],
["transformation", "transformations"],
["vibration", "vibrations"],
["wonder", "wonders"],
];
const bodyPart = [
"appendix",
"armpit",
"bile",
"central core",
"cheeks",
"chest",
"cuticles",
"diaphragm",
"fingernails",
"flaps",
"folds",
"frenulum",
"goiter",
"humors",
"limbs",
"lingam",
"legs",
"lungs",
"meatus",
"mind",
"paunch",
"perineum",
"ribs",
"spine",
"spleen",
"throat",
"uvula",
"yoni",
"wenis",
];
const affliction = [
"abasement",
"acne",
"appropriation",
"bodaciousness",
"cirrhosis",
"concavity",
"confiscation",
"corpulence",
"defenestration",
"denunciation",
"despondency",
"erosion",
"exasperation",
"fatigue",
"fibrillation",
"flaking",
"hardening",
"hypertension",
"ill-humor",
"imbalance",
"impertinence",
"impotence",
"impropriety",
"incontinence",
"indolence",
"ingrowth",
"itching",
"leprosy",
"malignancy",
"mastication",
"melancholia",
"miasma",
"mistreatment",
"mortification",
"necrosis",
"plasticity",
"protuberance",
"psoriasis",
"putrescence",
"rumination",
"schadenfreude",
"scruples",
"seizure",
"sinkage",
"softening",
"stoppage",
"swelling",
"whiffiness",
];
const verb = [
["appreciate", "appreciates", "appreciating"],
["blend", "blends", "blending"],
["bloom", "blooms", "blooming"],
["breathe", "breathes", "breathing"],
["carry", "carries", "carrying"],
["calm", "calms", "calming"],
["cleanse", "cleanses", "cleansing"],
["clear", "clears", "clearing"],
["consolidate", "consolidates", "consolidating"],
["cool", "cools", "cooling"],
["dance", "dances", "dancing"],
["descend", "descends", "descending"],
["discern", "discerns", "discerning"],
["exacerbate", "exacerbates", "exacerbating"],
["fold", "folds", "folding"],
["govern", "governs", "governing"],
["harmonize", "harmonizes", "harmonizing"],
["linger", "lingers", "lingering"],
["masticate", "masticates", "masticating"],
["nourish", "nourishes", "nourishing"],
["retract", "retracts", "retracting"],
["scatter", "scatters", "scattering"],
["shift", "shifts", "shifting"],
["sink", "sinks", "sinking"],
["soften", "softens", "softening"],
["tingle", "tingles", "tingling"],
["titillate", "titillates", "titillating"],
["ululate", "ululates", "ululating"],
["undulate", "undulates", "undulating"],
["vent", "vents", "venting"],
["vindicate", "vindicates", "vindicating"],
["warm", "warms", "warming"],
["waver", "wavers", "wavering"],
["weave", "weaves", "weaving"],
];
const healingMethod = [
["abate", "abates", "abating"],
["alleviate", "alleviates", "alleviating"],
["assuage", "assuages", "assuaging"],
["balance", "balances", "balancing"],
["broaden", "broadens", "broadening"],
["cool", "cools", "cooling"],
["discipline", "disciplines", "disciplining"],
["edify", "edifies", "edifying"],
["elevate", "elevates", "elevating"],
["enrich", "enriches", "enriching"],
["harmonize", "harmonizes", "harmonizing"],
["heal", "heals", "healing"],
["invigorate", "invigorates", "invigorating"],
["lift", "lifts", "lifting"],
["lighten", "lightens", "lightening"],
["mitigate", "mitigates", "mitigating"],
["promote", "promotes", "promoting"],
["re-balance", "re-balances", "re-balancing"],
["refine", "refines", "refining"],
["regenerate", "regenerates", "regenerating"],
["relieve", "relieves", "relieving"],
["soften", "softens", "softening"],
["sooth", "soothes", "soothing"],
["uplift", "uplifts", "uplifting"],
["warm", "warms", "warming"],
];
const recipe = [
"agarbatti",
"blend",
"composition",
"fragrance",
"incense",
"recipe",
"stick",
];
const amount = [
"base",
"blessing",
"boatload",
"crumb",
"dash",
"dollop",
"dusting",
"glob",
"hint",
"pinch",
"shred",
"smattering",
"smidgen",
"sprinkling",
"touch",
"trace",
];
const preposition = [
"above",
"across",
"after",
"against",
"along",
"amid",
"among",
"around",
"as",
"before",
"behind",
"below",
"beneath",
"beside",
"between",
"beyond",
"following",
"from",
"into",
"like",
"near",
"onto",
"opposite",
"over",
"past",
"round",
"through",
"to",
"toward",
"under",
"underneath",
"upon",
"versus",
"via",
"with",
"within",
];
const ingredient = [
"agarwood",
"amber",
"ambergris",
"ambrette seed",
"atlas cedar",
"bergamot",
"balsam of tolu",
"basil",
"bay laurel",
"benzoin",
"borneol camphor",
"burgundy pitch",
"calamus root",
"cardamom",
"cassia",
"catnip",
"chen pi",
"chamomile",
"cinnamon",
"clove",
"colophony pine",
"copal",
"coriander",
"dammar",
"dragons blood",
"elemi",
"eucalyptus",
"frankincense",
"galangal root",
"galbanum",
"ginger root",
"guggul",
"hibiscus",
"hyssop",
"juniper berry",
"juniper",
"labdanum",
"laurel leaf",
"lavender",
"lemon balm",
"lemon grass",
"marjoram",
"mastic",
"mugwort",
"musk root",
"myrrh",
"nutmeg",
"oakmoss",
"onycha",
"opoponax",
"orris root",
"palo santo",
"patchouli",
"pine needle",
"pine resin",
"red cedar",
"red sandalwood",
"rose",
"rosemary",
"saffron",
"sage",
"sandalwood",
"sandarac",
"spikenard",
"spruce",
"star anise",
"storax",
"sweetgrass",
"thyme",
"tolu balsam",
"tonka bean",
"turkey rhubarb",
"turmeric",
"valerian",
"vanilla",
"vetiver",
"weeping cypress",
];
const getWord = (wordArr, usedArr, tense, plural) => {
if (wordArr == noun) {
let word = wordArr[(wordArr.length * Math.random()) | 0][0];
let pluralWord = wordArr[(wordArr.length * Math.random()) | 0][1];
let wordIsUsed = usedArr.indexOf(word) >= 0 ? true : false;
let pluralIsUsed = usedArr.indexOf(pluralWord) >= 0 ? true : false;
while (wordIsUsed == true || pluralIsUsed == true) {
word = wordArr[(wordArr.length * Math.random()) | 0][0];
pluralWord = wordArr[(wordArr.length * Math.random()) | 0][1];
wordIsUsed = usedArr.indexOf(word) >= 0 ? true : false;
pluralIsUsed = usedArr.indexOf(word) >= 0 ? true : false;
}
return plural ? pluralWord : word;
} else if (wordArr == verb || wordArr == healingMethod) {
let word = wordArr[(wordArr.length * Math.random()) | 0][0];
let present = wordArr[(wordArr.length * Math.random()) | 0][1];
let continuous = wordArr[(wordArr.length * Math.random()) | 0][2];
let wordIsUsed = usedArr.indexOf(word) >= 0 ? true : false;
let presentIsUsed = usedArr.indexOf(present) >= 0 ? true : false;
let continuousIsUsed = usedArr.indexOf(continuous) >= 0 ? true : false;
while (
wordIsUsed == true ||
presentIsUsed == true ||
continuousIsUsed == true
) {
word = wordArr[(wordArr.length * Math.random()) | 0][0];
present = wordArr[(wordArr.length * Math.random()) | 0][1];
continuous = wordArr[(wordArr.length * Math.random()) | 0][2];
wordIsUsed = usedArr.indexOf(word) >= 0 ? true : false;
presentIsUsed = usedArr.indexOf(word) >= 0 ? true : false;
continuousIsUsed = usedArr.indexOf(continuous) >= 0 ? true : false;
}
if (tense == "present") {
return present;
} else if (tense == "continuous") {
return continuous;
} else {
return word;
}
} else {
let word = wordArr[(wordArr.length * Math.random()) | 0];
let isUsed = usedArr.indexOf(word) >= 0 ? true : false;
while (isUsed == true) {
word = wordArr[(wordArr.length * Math.random()) | 0];
isUsed = usedArr.indexOf(word) >= 0 ? true : false;
}
return word;
}
};
const capitalize = (str) => {
return str[0].toUpperCase() + str.slice(1);
};
const coinFlip = () => {
return Math.random() < 0.5 ? true : false;
};
const randomNumber = (min, max) => {
return Math.floor(Math.random() * (max - min + 1) + min);
};
const descriptionConstructor = (
adjective,
noun,
bodyPart,
verb,
preposition,
ingredient,
) => {
let used = [];
let description = "<p>";
let currentWord = "";
const addWord = (list, plural, punctuation, tense, capitalizeBool) => {
currentWord = getWord(list, used, tense, plural);
capitalizeBool
? (description += capitalize(currentWord))
: (description += currentWord);
description += punctuation;
used.push(currentWord);
};
const addAdjective = (punctuation, capitalize) => {
addWord(adjective, false, punctuation, null, capitalize);
};
const addAffliction = (punctuation, capitalize) => {
addWord(affliction, false, punctuation, null, capitalize);
};
const addAmount = (punctuation, capitalize) => {
addWord(amount, false, punctuation, null, capitalize);
};
const addBodyPart = (punctuation, capitalize) => {
addWord(bodyPart, false, punctuation, null, capitalize);
};
const addHealingMethod = (punctuation, tense, capitalize) => {
addWord(healingMethod, false, punctuation, tense, capitalize);
};
const addIngredient = (punctuation, capitalize) => {
addWord(ingredient, false, punctuation, null, capitalize);
};
const addNoun = (punctuation, plural, capitalize) => {
addWord(noun, plural, punctuation, null, capitalize);
};
const addPreposition = (punctuation, capitalize) => {
addWord(preposition, false, punctuation, null, capitalize);
};
const addRecipe = (punctuation, capitalize) => {
addWord(recipe, false, punctuation, null, capitalize);
};
const addVerb = (punctuation, tense, capitalize) => {
addWord(verb, false, punctuation, tense, capitalize);
};
const insertCustomString = (str) => {
description += str;
};
const removeLastCharacter = () => {
description = description.slice(0, description.length - 1);
};
// Construct opening sentence.
addAdjective(" ", true);
addNoun(" ");
addVerb(" ", "present");
if (coinFlip()) addPreposition(" ");
addVerb(" ", "continuous");
coinFlip() ? addNoun(". ", true) : addNoun(". ");
insertCustomString("</p>");
// Construct recipe description
insertCustomString("<p>This ");
addAdjective(" ");
addRecipe(" ");
addVerb(" ", "present");
insertCustomString("and ");
addVerb(" ", "present");
addPreposition(" ");
addNoun(" ");
insertCustomString("and ");
addNoun(".</p>");
// Construct ingredient roles
const numOfIngredients = randomNumber(2, 5);
for (let i = 0; i < numOfIngredients; i++) {
i == 0 ? insertCustomString("<p>") : null;
if (coinFlip()) {
insertCustomString("A ");
addAmount(" ");
insertCustomString("of ");
addAdjective(" ");
} else {
addAdjective(" ", true);
}
if (coinFlip()) {
if (coinFlip()) {
insertCustomString("and ");
addAdjective(" ");
addIngredient(" ");
} else {
removeLastCharacter();
insertCustomString(", yet ");
addAdjective(", ");
addIngredient(" ");
}
} else {
addIngredient(" ");
}
addVerb(" ", "present");
if (coinFlip()) {
insertCustomString("and ");
addVerb(" ", "present");
}
if (coinFlip()) {
if (coinFlip()) {
insertCustomString("while ");
addHealingMethod(" ", "continuous");
addAffliction(" ");
insertCustomString("of the ");
} else {
insertCustomString("and ");
addHealingMethod(" ", "present");
addAffliction(" ");
insertCustomString("of the ");
}
i == numOfIngredients - 1 ? addBodyPart(".</p>") : addBodyPart(". ");
} else {
i == numOfIngredients - 1 ? addNoun(".</p>") : addNoun(". ");
}
}
// Healing properties
insertCustomString("<p>Also good for: ");
const numOfHealingProps = randomNumber(1, 5);
for (let i = 1; i <= numOfHealingProps; i++) {
i == 1 ? addAffliction(" ", true) : addAffliction(" ");
insertCustomString("of the ");
if (i == numOfHealingProps - 1) {
addBodyPart(", and ");
} else if (i == numOfHealingProps) {
addBodyPart(".</p>");
} else {
addBodyPart(", ");
}
}
return description;
};

6
public/js/three.min.js vendored Normal file

File diff suppressed because one or more lines are too long