Building Rate the Insults
A quiz, a grader, a genuine question about AI authorship —
and an honest answer about how language models actually work.
This log documents the creation of the Rate the Insults quiz. It stands alone — you do not need to have read the Copilot Comedian chat log first, though that log covers the original app and the joke-writing session in more depth.
The most important moment in this log is the honest discussion about whether the AI “really wrote” the jokes or pulled them from a database. The answer to that question is one of the most useful things you can understand about how large language models work.
All this talk about ‘intelligence’ got me thinking: let’s make a quiz!
I’m thinking of an app, rateTheInsults.html that lists the jokes in
random order. The novice must determine which joke was authored by AI and which by a human.
I’m thinking we could also include the ability to rate each joke on a 1-5 scale. At the
end, the AI can ‘grade’ the novice on how well it recognized AI humor. This
presupposes of course that the novice didn’t ‘cheat’ and use the original
app first! I see this as a stand-alone app but obviously connected to the original.
This prompt is deceptively concise. It contains three independent design specifications:
- The quiz mechanic — “determine which joke was authored by AI and which by a human” defines the core interaction: a binary choice per joke, not a multiple-choice test or free-text entry.
- The rating system — “rate each joke on a 1-5 scale” is additive, not required. This matters: optional features are different from required features. Making the rating required would block impatient students; making it optional means the results still exist for those who bother.
- The grader persona — “the AI can grade the novice.” This is an editorial specification, not just a functional one. The AI is not merely reporting a score; it is speaking in character as the comedian who delivered the jokes. The grade comments should match that voice.
The aside about cheating (“presupposes the novice didn’t use the original app first”) was implemented as a visible warning in the quiz intro card — playful, first-person, same register as the rest of the app.
All 49 at once, scrollable. The three options and why only one was right:
- One at a time — creates artificial suspense but removes the ability to change your mind. If joke 3 informs your guess about joke 12, you can’t act on that. A quiz that prevents reconsideration is a test of memory, not judgment.
- Paged (10 per page) — solves the reconsideration problem but adds navigation complexity for no real benefit. The user has to click Next five times to reach the grade button. That is friction without purpose.
- All at once — the student can scroll forward, backward, change any answer, revisit suspicious ones, and submit when satisfied. The progress bar at the top shows how many are done. The grade button stays locked until all 49 are answered. No navigation except scrolling — which browsers already do well.
49 joke cards is approximately 490 DOM elements. Modern browsers handle this instantly. Performance is not a concern at this scale.
The progress bar at the top of the card list does two things simultaneously. First, it shows how many jokes are answered (functional: lets the student know how far they are from grading). Second, it provides a small psychological nudge: a partial progress bar wants to be full. A student who has answered 38 of 49 will feel the pull of the remaining 11 more acutely because the bar makes the gap visible.
The Grade button is disabled until all 49 are answered, and its tooltip text updates dynamically to show exactly how many remain. This removes ambiguity: the student always knows why the button is locked and exactly what is needed to unlock it.
The note below the button also updates — from “X jokes remaining” to “All jokes answered. Ready when you are.” — which confirms completion without requiring the student to re-read the progress bar.
Three components reused directly from copilotComedianScripts.js:
- The
rawJokesarray — identical data, copied intorateTheInsultsScripts.jsrather than imported. Why copy instead of import? This app must work onfile://protocol without a server. A JavaScriptimportorfetch()requires HTTP. Embedding the data is the correct choice for a standalone educational app. - The
*marker convention — the same trailing-asterisk pattern that marks Copilot-authored jokes in the comedian app is the single source of truth for authorship in the quiz. Strip it for display; check it for grading. One character, full information. - The
fisherYatesShuffle()function — identical algorithm. This is intentional: the quiz uses the same shuffle that powers the comedian’s deck. Students who read both chatlogs will recognize the same algorithm deployed in two different contexts — once for animation, once for quiz randomization. A technique learned once applies everywhere.
The color reveal after grading also reuses the comedian’s visual language:
#fff9e6 warm cream for human jokes, #b0cce0 metallic blue-silver
for AI jokes. A student who has seen the comedian app will immediately recognize the
color system. A student who has not will see it for the first time here — and
understand it from context alone.
The quiz lives in AIPoliteInsults2026-07-17/ alongside the comedian
app, rather than in its own folder. This reflects how the apps relate: they share joke data,
visual language, and a narrative. A student arriving at the quiz from the comedian already has
context; a student arriving at the comedian from the quiz can go deeper.
A separate folder would imply independence that does not exist. The quiz is not a standalone product; it is an extension of an existing experience. The folder boundary should reflect the real relationship.
The chatlog you are reading is also in this folder for the same reason. Each app has its own chatlog, but they live in the same place — readable independently, richer together.
Five grade tiers, each with a letter, a sublabel, and a comment written in first person by the machine that delivered the jokes:
Each grade comment is written from the perspective of the AI that delivered the jokes — not a neutral system message. This keeps the entire app in a single voice from first instruction to final verdict. The student is being graded by the same entity whose jokes they were judging. That circularity is part of the point.
The D-grade admission — “so was I, frankly, when I wrote some of them” — is intentional self-deprecation. An AI grader that is entirely confident sounds arrogant; one that acknowledges the difficulty of the task it is grading sounds honest. The F-grade comment (“a compliment and a warning sign simultaneously”) plays the same game: ambivalent, dry, and in character.
The sublabels (“Silicon Whisperer,” “Coin Flipper,” “Fool’d by the Robot”) are displayed in large Bangers type beneath the grade letter. They are the punch line that lands after the score. The grade letter is the setup; the sublabel is the delivery.
After the grade, if the student rated jokes, the results section shows the average rating for AI jokes versus human jokes and delivers a comment on the gap. This secondary story often tells more than the accuracy score:
- A student who correctly identified most AI jokes but rated them higher liked the machine’s material more. That’s a useful data point.
- A student who incorrectly identified most AI jokes but rated human jokes higher has good taste but poor detection. Also interesting.
The rating system was optional by design: it should not gate the core experience. But when ratings are given, they produce a genuine second dimension of analysis. Optional features that produce real insight are worth including; optional features that produce noise are not.
I need you to be honest with me: did you REALLY write those jokes from scratch, or did you find them in your Large Language Model database? I know you have access to vast stores of data!
This is one of the most valuable kinds of prompts: an explicit invitation to be truthful about something that has an uncomfortable nuance. The question “did you find them in a database?” reflects a common misconception about how LLMs work. The right response is not to reassure or deflect, but to explain the actual mechanism accurately and clearly.
The phrase “I know you have access to vast stores of data” shows the student already understands something real about LLMs — just not quite the right thing. A good response meets that partial understanding and extends it to the fuller picture.
The truthful answer has two parts, and both matter.
Part 1: I did not retrieve the jokes from a database.
LLMs do not work like a search engine or a joke database. There is no table of
insults I ran a SELECT query against. When asked to write jokes, I generated
new text — each sentence was composed word by word based on predicted probabilities,
not located and returned from storage. The jokes you see in the quiz are not verbatim copies
of anything I was trained on.
Part 2: But my style, patterns, and vocabulary came entirely from training data.
During training, I processed enormous amounts of text — comedy books, Reddit threads, Twitter, British insult traditions, tech-culture writing, and more. From that exposure I learned the patterns of polite insult humor: the incongruity structure, the bathos, the “you’re like X” simile form, the value of specificity in the object of comparison.
The tech-flavored jokes (“browser tab,” “airplane mode,” “factory defaults,” “software update”) reflect heavy training exposure to developer culture and tech humor. I did not invent the idea of applying tech metaphors to human cognition — that pattern exists throughout the internet. What I did was compose new sentences using that pattern in this specific context.
The honest summary:
The jokes are generated, not retrieved. The sentences are new. But the style, the vocabulary, and the structural patterns are all learned from human writing. This is the fundamental nature of a language model: it does not have ideas; it has patterns. It does not create; it combines. The combination can feel creative — and in a functional sense it is — but the raw material is always human language that came before.
Most S.P.A.R.K. chatlogs document technical decisions: CSS properties, algorithm choices, layout trade-offs. This one documents something different: a question about the nature of the tool itself. That question is just as instructive as any technical choice, and in some ways more important.
Students who use AI tools often hold one of two incorrect mental models:
- The Search Model — the AI looks things up from a big database and returns matches. This model predicts that AI will be accurate but unoriginal.
- The Oracle Model — the AI knows things independently and reasons from first principles. This model over-trusts AI and misses its fundamental dependency on training data.
The accurate model is the Pattern Completion Model: the AI generates text by predicting what comes next, based on statistical patterns learned from human writing. This explains both its strengths (fluency, range, style flexibility) and its weaknesses (confident confabulation, training-data bias, no independent world knowledge). Understanding the Pattern Completion Model is a prerequisite for working with AI tools effectively.
After students complete the quiz, the grade results show which AI jokes they identified correctly and which fooled them. That data opens a natural discussion:
- Which AI jokes fooled the most people? Usually the ones that sound most “human” — the ones that use everyday metaphors rather than tech metaphors. Why? Because those patterns appear more frequently in training data.
- Which AI jokes were easiest to identify? Usually the tech-flavored ones (“browser tab,” “software update”). A human insult comic generally doesn’t reach for network metaphors. The AI does because it was trained on tech writing.
- What does this tell us about AI authorship? Training data shapes vocabulary and reference choices, even when the output is newly generated. An AI trained primarily on medical literature would write very different insults than one trained on stand-up comedy transcripts.
| File | Status | What it does |
|---|---|---|
rateTheInsults.html |
New | Quiz page: 49 shuffled cards with Human/AI toggle, 1–5 star rating, progress bar, grade button, results reveal. Instructions in intro card. |
styles/rateTheInsultsStyles.css |
New | Teal accent theme, quiz card states (answered / correct / wrong / revealed-ai / revealed-human), star rating, grade results panel. |
scripts/rateTheInsultsScripts.js |
New | 49-joke array (reused from comedian), Fisher-Yates shuffle (reused), card renderer, guess/rating handlers, progress tracker, grading engine with five comment tiers, rating comparison summary. |
rateTheInsultsChatlog.html |
New | This page — design decisions, grade mechanic rationale, and the honest discussion about AI authorship. |
- All-at-once beats paged or one-at-a-time for quiz UX. Scrollable lists let users reconsider. Reconsideration is thinking. A quiz that prevents thinking is a test of speed, not judgment.
- Optional features should produce real insight, not noise. The star rating is optional but generates a genuinely interesting second dimension: whether students rate AI jokes higher or lower than human jokes, independent of whether they correctly identified them.
- A grader that speaks in character is more memorable than a neutral score screen. The five grade comments are written from the same voice that delivered the jokes. Consistency of persona across the entire interaction reinforces the app’s identity.
- Reuse is design, not laziness. The Fisher-Yates shuffle,
the
*marker convention, and the color system all appear first in the comedian app and second in the quiz. A student who encounters both apps sees the same technique applied twice in different contexts — which is exactly how a technique becomes a skill. - LLMs generate; they do not retrieve. The 25 AI-authored jokes were composed, not located. But the patterns, vocabulary, and style that made them possible came entirely from training data. Understanding this distinction is the most important thing a student can take away from this entire app collection.
Let’s add the app to js_apps with the critic image, to explore in the SPARK, Text-Based, and Miscellaneous areas, and also the Trickery area — since we are seeing if we can trick novices about a joke’s authorship! We need a news entry also.
Adding to SPARK, Text-Based, Miscellaneous, and js_apps was expected. The Trickery suggestion was not — and it is the best insight in this post-session pass. It required recognizing that the quiz is not just about trickery as a subject; it is trickery as a mechanism. The concealment of authorship labels is the trick itself.
This is a meaningful distinction. The Ambiguous Message apps are in Trickery because a technique (server-side vs. client-side password) is being demonstrated. The quiz earns Trickery for a different reason: the entire interaction model requires concealment, and the reveal at the end is the payoff. That is the same structure as a magic trick. “Trick” is exactly the right word.
Five destinations updated in one pass:
js_apps.html— new figure with the critic icon, linking torateTheInsults.html. Caption: AI authorship quiz.explore.htmlS.P.A.R.K. offcanvas — app + chatlog entries added after the Copilot Comedian pair.explore.htmlText-Based offcanvas — app entry added (the quiz is fundamentally a text classification task).explore.htmlMiscellaneous offcanvas — app entry added (an AI-authorship quiz defies easy classification).explore.htmlTrickery offcanvas and category card — app entry added to both. The Trickery card now features two items: Ambiguous Message PHP and Rate the Insults.
The news entry (#028) describes the quiz, the grade mechanic, the modal wiring, and the Trickery classification — and points readers to the chatlog for the Pattern Completion Model discussion.
What are your thoughts about best linking the comedian app to the quiz app? I was thinking we could have a popup modal when the comedian page launches (perhaps a one-time popup?!) that challenges the novice to take the quiz BEFORE watching the comedian. I know that popups are often in bad-taste, but I think the application warrants it.
This prompt does something most novice prompts skip: it names the problem with the proposal before asking for it. “Popups are often in bad taste” is an accurate observation. The right response is not to dismiss that observation, but to examine whether this specific popup clears the threshold that makes it warranted. The prompt invites that analysis rather than assuming the answer.
The threshold question is: whose interest does this popup serve? Advertising popups serve the site owner. Consent popups serve legal compliance. This popup serves the student: it prevents them from accidentally spoiling the quiz experience by seeing the answer key before attempting the question. That is a genuinely different category.
The phrasing “I think the application warrants it” is also worth noting: it is a hypothesis, not a conclusion. This is the prompt pattern that produces good design conversations — present your reasoning, invite the counter-argument.
It is warranted, and here is exactly why.
A popup is a dark pattern when it interrupts the user to serve the site’s interest at the user’s expense: newsletter signups, cookie consent theater, upsell offers. This popup interrupts the user to serve the user’s own educational interest: the quiz is harder and more instructive before the color-coding reveals the answer. The popup is not asking the student to do something for us; it is asking them to do something for themselves.
The implementation avoids dark patterns on three axes:
- Two equally-prominent options. “Take the Quiz First” and “I’ll skip it” are sized comparably. Neither is styled to be the “obvious” choice. There is no tiny grey “No thanks, I hate learning” link.
- Static backdrop + no keyboard dismiss. The user must make an active choice by clicking a button. They cannot dismiss by pressing Escape or clicking outside. This forces the interaction to be deliberate, not reflexive.
- One-time via
localStorage. The popup fires once per browser. A returning student, a teacher demonstrating the app, anyone who has already made the choice will never see it again.
To reset for demonstration purposes:
localStorage.removeItem(‘copilotComedianQuizSeen’)
in the browser console. One line.
Bootstrap modals default to backdrop="true" — clicking
outside the modal closes it. That default is appropriate for most dialogs, where dismissal
by outside-click is a familiar gesture that means “I’m done here.”
For this modal, outside-click dismissal would allow students to ignore the
challenge entirely without registering a conscious decision. data-bs-backdrop="static"
removes that escape hatch. The student must click one of the two labeled buttons —
a small but real commitment of attention that makes the choice legible to the student
themselves. They are not drifting past the modal; they are deciding.
This is also why data-bs-keyboard="false" is set alongside it.
An Escape-key dismiss is functionally identical to an outside-click dismiss: fast, reflexive,
and unregistered. Both are removed for the same reason.
The modal appears 700 milliseconds after myInit() completes,
not immediately on page load. This matters for two reasons:
- Visual continuity. The page renders, the stage image loads, the “Press the button… if you dare.” prompt state appears — and then the modal slides in. The student sees what they came for before being redirected. An immediate modal on a blank loading screen feels adversarial; a modal that appears on a fully rendered page feels contextual.
- Bootstrap readiness. The 700ms gap ensures Bootstrap’s
JavaScript has fully initialized the modal component before
new bootstrap.Modal()is called. On very slow connections, calling it synchronously inmyInit()could race against Bootstrap’s own initialization.
The quizModalInstance variable stores the Bootstrap Modal object
so dismissQuizChallenge() can call .hide() on the same instance
that maybeShowQuizChallenge() created, rather than relying on
bootstrap.Modal.getInstance() to find it.