This commit is contained in:
Nicky Case 2018-09-18 13:17:42 -04:00
commit 4670ff8e93
29 changed files with 3442 additions and 0 deletions

508
ch2.html Normal file
View file

@ -0,0 +1,508 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>An Interactive Comic</title>
<link rel="stylesheet" type="text/css" href="css/comic.css"/>
</head>
<body>
<div id="comic">
<!-- - - - - - - - - - - - -->
<!-- THE ART of SRS - - - - -->
<!-- - - - - - - - - - - - -->
<!-- Part 1.1: Leitner Calendar -->
<panel w=500 h=450>
<pic></pic> <!-- hey susan, TRAINERS -->
<words x=10 y=10 w=430 h=60>
You don't <i>have</i> to use a shoebox for Spaced Repetition,
but it's funnier if you do.
</words>
<words x=30 y=350 w=430 h=60>
(Later, we'll look at some Spaced Repetition <i>apps</i>,
like Anki &amp; Tinycards)
</words>
</panel>
<panel w=500 h=400>
<pic></pic> <!-- poker: me vs brain -->
<words x=10 y=10 w=430 h=60>
This method is called The Leitner System.
It's like a card game you play against yourself!
</words>
<words x=30 y=300 w=430 h=60>
First, divide your box up into seven "Levels".
Each Level will store some flashcards.
</words>
</panel>
<panel w=400 h=200 bg="#bada55">
"wait, what's a flashcard?"
<!-- This is, ya goof -->
<!--<sim x=0 y=0 w=600 h=400 src="sims/multicard/?cards=sci_a,sci_b,sci_c"></sim>-->
</panel>
<panel w=400 h=200 bg="#bada55">
"what kind of stuff can I learn with flashcards?"
<!-- This is, ya goof -->
<!--<sim x=0 y=0 w=600 h=400 src="sims/multicard/?cards=sci_a,sci_b,sci_c"></sim>-->
</panel>
<panel w=600 h=80>
<words w=600 x=-15 no-bg>
We need to space out our recalls with <i>increasing gaps of time</i>.
So, here's a calendar of when to review which Level's cards:
</words>
</panel>
<panel w=600 h=300 bg="#bada55">
<!-- Note: Level 4 THEN 2 THEN 1... -->
</panel>
<!-- Part 1.2: Leitner Rules -->
<panel w=600 h=80>
<words w=600 x=-15 no-bg>
But how do cards move <i>between</i> Levels?
How do you play this game of Spaced Repetition Solitaire?
</words>
</panel>
<panel w=500 h=400>
<pic></pic> <!-- joyful baby card... and grisly card -->
<words x=10 y=10 w=430 h=60>
The rules are simple.
First, all new cards start at Level 1.
</words>
<words x=30 y=300 w=430 h=60>
(If you're new to Spaced Repetition, I recommend starting with 5 new cards a day.)
</words>
</panel>
<panel w=500 h=430>
<pic></pic> <!-- valhalla -->
<words x=10 y=10 w=430 h=90>
When you review the cards in a Level, first, shuffle them.
Then, try to recall them.
Each card you get right goes up one Level.
</words>
<words x=30 y=330 w=430 h=60>
(If you're already at the final Level, congrats!
Your card gets to retire in Valhalla)
</words>
</panel>
<panel w=500 h=400>
<pic></pic> <!-- HIGH STAKES -->
<words x=10 y=10 w=430 h=60>
But each card you get wrong... goes
<i>all the way back down to Level 1.</i>
</words>
<words x=30 y=300 w=430 h=60>
(You can change the rules it's <i>your</i> shoebox
but I recommend playing it this way)
</words>
</panel>
<panel w=500 h=400>
<pic></pic> <!-- baby + REMEMBER ME?! -->
<words x=10 y=10 w=430 h=60>
Each day, you review Level 1 at the end.
You'll see your new cards + the cards you forgot.
</words>
<words x=30 y=300 w=430 h=60>
Keep trying to recall them, until you can get <i>every one</i> right!
Move them all to Level 2.
</words>
</panel>
<panel w=600 h=80>
<words w=600 x=-15 no-bg>
And that's it!
Here's how the game plays out over several days:
(Later, we'll see a sim for several <i>months</i>)
</words>
</panel>
<panel w=600 h=300 bg="#bada55">
</panel>
<!-- Part 1.3: Leitner Full Sim -->
<panel w=600 h=110>
<words w=600 x=-15 no-bg>
Each daily session takes 20-30 minutes.
Instead of watching a TV episode, you could play a card game
and remember anything you want <i>for life</i>.
</words>
</panel>
<panel w=500 h=400>
<pic></pic> <!-- snowball -->
<words x=10 y=10 w=430 h=60>
However, habits are hard. If you start big, you won't get the ball rolling...
</words>
<words x=30 y=300 w=430 h=60>
But if you start <i>small</i>, you can gain momentum,
and roll your snowball bigger and bigger.
</words>
</panel>
<panel w=500 h=690>
<pic></pic> <!-- katamari damacy -->
<words x=10 y=10 w=430 h=60>
That's why I recommend <i>starting</i> with 5 new cards a day.
</words>
<words x=30 y=300 w=430 h=60>
Once you're comfortable with that, you can do 10 new cards/day.
Then 15. Then 20, 25, 30.
</words>
<words x=10 y=590 w=430 h=60>
And at 30 new cards a day, you can learn <i>10,000+</i> new facts/words/etc a <i>year.</i>
</words>
</panel>
<panel w=600 h=50>
<words w=600 x=-15 no-bg>
Here's what that looks like, over several months:
</words>
</panel>
<panel w=600 h=300 bg="#bada55">
</panel>
<!-- Part 1.3: Leitner Outro -->
<panel w=600 h=110>
<words w=600 x=-15 no-bg>
That's it. That's how you can make long-term memory a <i>choice</i>.
</words>
<words w=600 x=-15 y=50 no-bg>
Let's let that sink in. Take a break, and recall what we just learnt:
</words>
</panel>
<panel w=600 h=400 bg="#bada55">
<!--<sim x=0 y=0 w=600 h=400 src="sims/multicard/?cards=sci_a,sci_b,sci_c"></sim>-->
</panel>
<!-- Part 2.1: Pitfalls -->
<panel w=600 h=80>
<words w=600 x=-15 no-bg>
Spaced Repetition almost seems too good to be true.
<br>
And it is... <i>IF</i> you fall for 3 very common pitfalls.
</words>
</panel>
<panel w=500 h=450>
<pic></pic> <!-- Library, humor -->
<words x=10 y=10 w=430 h=60>
Spaced Repetition will fail if your cards feel
bloated, disconnected, or meaningless.
</words>
<words x=30 y=350 w=430 h=60>
Memory isn't a private library,
where you hoard a bunch of random books to impress others.
</words>
</panel>
<panel w=500 h=450>
<pic></pic> <!-- ANGRY FACE -->
<words x=10 y=10 w=430 h=90>
Memory is more like a jigsaw puzzle, full of tiny pieces joined together.
(This is also how neurons work: lots of tiny, connected things)
</words>
<words x=30 y=380 w=430 h=30>
It's not about <i>collection</i>, it's about <i>connection</i>.
</words>
</panel>
<panel w=500 h=400>
<pic></pic>
<words x=10 y=10 w=410 h=60>
So, to get the most out of Spaced Repetition,
you must make your cards...
</words>
<!--SMALL, CONNECTED, and MEANINGFUL.-->
</panel>
<!-- Part 2.2: Small -->
<panel w=600 h=100 bg="#000">
<words x=10 y=10 w=400>
<b>SMALL</b>
</words>
</panel>
<panel w=450 h=50>
<words w=450 x=-15 no-bg>
This card sucks:
</words>
</panel>
<panel w=400 h=200 bg="#bada55">
<!-- Mitochrondria? -->
<!--<sim x=0 y=0 w=600 h=400 src="sims/multicard/?cards=sci_a,sci_b,sci_c"></sim>-->
</panel>
<panel w=450 h=350>
<pic></pic>
<words x=10 y=10 w=400 h=30>
It's too big. Too much information.
</words>
<words x=10 y=250 w=400 h=60>
Instead, let's cut in up into a bunch of smaller, connected pieces,
like so:
</words>
</panel>
<panel w=400 h=600 bg="#bada55">
<!--<sim x=0 y=0 w=600 h=400 src="sims/multicard/?cards=sci_a,sci_b,sci_c"></sim>-->
</panel>
<panel w=450 h=90>
<words w=450 x=-15 no-bg>
Facts connect to facts.
But there's other, more playful ways for cards to be...
</words>
</panel>
<!-- Part 2.3: Connected -->
<panel w=600 h=100 bg="#000">
<words x=10 y=10 w=400>
<b>CONNECTED</b>
</words>
</panel>
<panel w=450 h=110>
<words w=450 x=-15 no-bg>
This card is... alright.
It's an English word on the front, French word on the back.
It's the standard for most flashcards:
</words>
</panel>
<panel w=400 h=200 bg="#bada55">
<!--<sim x=0 y=0 w=600 h=400 src="sims/multicard/?cards=sci_a,sci_b,sci_c"></sim>-->
</panel>
<panel w=450 h=400>
<pic></pic>
<words x=10 y=10 w=400 h=60>
But you know what would make it stick in memory better?
</words>
<words x=10 y=300 w=400 h=60>
If you connected it to
<i>images, sounds, context, and/or personal details!</i>
Like so:
</words>
</panel>
<panel w=400 h=200 bg="#bada55">
<!-- comically FAT cat -->
<!--<sim x=0 y=0 w=600 h=400 src="sims/multicard/?cards=sci_a,sci_b,sci_c"></sim>-->
</panel>
<panel w=600 h=300>
<pic></pic>
<words x=10 y=10 w=200>
The front now has a drawing of a cat (image)
with a fill-in-the-blank French sentence (context: grammar)
about my childhood cat, Stripes. (personal)
</words>
</panel>
<panel w=600 h=300>
<pic></pic>
<words x=10 y=10 w=200>
The back now has a symbol of the noun's gender (image),
its pronunciation (sound*),
and a warning not to use the female version. (context: slang)
</words>
<words w=330 x=240 y=230 no-bg fontsize=20>
* Obviously, paper cards can't play sounds.
But apps like Anki/Tinycards can!
</words>
</panel>
<panel w=450 h=90>
<words w=450 x=-15 no-bg>
But the <i>most</i> important connection of all,
is to connect your learning to something that is...
</words>
</panel>
<!-- Part 2.4: Meaningful -->
<panel w=600 h=100 bg="#000">
<words x=10 y=10 w=400>
<b>MEANINGFUL</b>
</words>
</panel>
<panel w=500 h=350>
<pic></pic> <!-- examples: programming, ukulele, french -->
<words x=10 y=10 w=450 h=60>
Personally, here's how I've learnt best:
First, I try to <b>do</b> something.
</words>
</panel>
<panel w=500 h=350>
<pic></pic> <!-- examples: programming, ukulele, french -->
<words x=10 y=10 w=450 h=60>
Inevitably, I'll get stuck.
In that moment, I'll look up what I need,
and <b>learn</b> something.
</words>
</panel>
<panel w=400 h=200 bg="#bada55">
<!--
..and then back to doing.
-->
<!--
...and then back to learning.
-->
</panel>
<panel w=450 h=30>
<words w=450 x=-15 y=-15 no-bg>
And so on. <!-- FADE PANEL -->
</words>
</panel>
<panel w=450 h=30>
<words w=450 x=-15 y=-15 no-bg>
And so on. <!-- FADE PANEL -->
</words>
</panel>
<panel w=450 h=30>
<words w=450 x=-15 y=-15 no-bg>
And so on. <!-- FADE PANEL -->
</words>
</panel>
<panel w=450 h=400>
<pic></pic> <!-- connected to HEART -->
<words x=10 y=10 w=400 h=60>
That, I believe, is the best way to keep yourself motivated while learning:
</words>
<words x=10 y=300 w=400 h=60>
By making sure your learning is in service of <i>something you actually care about.</i>
</words>
</panel>
<panel w=600 h=90>
<words w=600 x=-15 no-bg>
Speaking of learning, let's practice recalling what we've learnt:
(this is the second-last time!)
</words>
</panel>
<panel w=600 h=400 bg="#bada55">
<!--<sim x=0 y=0 w=600 h=400 src="sims/multicard/?cards=sci_a,sci_b,sci_c"></sim>-->
</panel>
<!-- Part 2.5: Outro -->
<panel w=500 h=450>
<pic></pic> <!-- show three rules -->
<words x=10 y=10 w=370 h=30>
These three rules for making cards...
</words>
<!-- SMALL, CONNECTED, MEANINGFUL -->
<words x=30 y=320 w=430 h=90>
...are why it's consensus among the Spaced Repetition community that,
for the most part,
<b>you should make your own cards.</b>
</words>
</panel>
<panel w=400 h=400>
<pic></pic> <!-- me, talking -->
<words x=10 y=10 w=300 no-bg>
This way, you can connect facts to things <i>you</i> know,
to images and sounds <i>you</i> like,
in service of something <i>you</i> love.
</words>
</panel>
<panel w=400 h=400>
<pic></pic>
<words x=10 y=10 w=300 no-bg>
That's why, in the <i>final</i> part of this interactive comic,
you're going to make your own cards!
</words>
<words x=30 y=160 w=170 no-bg>
And these cards will be about...
</words>
</panel>
<panel w=400 h=400>
<pic></pic> <!-- goofy pic -->
<words x=130 y=10 w=230 fontsize=150 no-bg>
YOU
</words>
</panel>
</div>
<!-- - - - - - - - - - -->
<!-- SIMULATION LABELS -->
<!-- - - - - - - - - - -->
<div id="labels">
<div id="default_labels">
<span id="leitner_day">
Day [N]
</span>
<span id="leitner_step_to_review">
to review: Level
</span>
<span id="leitner_step_reviewing">
review Level [N]
</span>
<span id="leitner_step_new">
add [N] new cards
</span>
<span id="leitner_step_stats">
total: [N] cards!
</span>
<span id="leitner_step_stats_2">
([N] in very-long-term memory)
</span>
</div>
</div>
</body>
</html>
<script src="js/minpubsub.src.js"></script>
<script src="js/comic.js"></script>

BIN
css/PatrickHand-Regular.ttf Executable file

Binary file not shown.

95
css/comic.css Normal file
View file

@ -0,0 +1,95 @@
/* FONT FACE */
@font-face {
font-family: "PatrickHand";
font-style: normal;
font-weight: 400;
src: url(PatrickHand-Regular.ttf) format('truetype');
}
/* HTML & BODY */
html, body{
width:100%;
height:100%;
}
body{
background: #eeeeee;
margin:0;
font-family: "PatrickHand", Helvetica, Arial;
font-size: 25px;
line-height: 1.2em;
}
/* fake bold */
b, strong{
font-weight: normal;
text-shadow:1px 0 0 currentColor;
letter-spacing: 1px;
}
/*********/
/* COMIC */
/*********/
#comic{
overflow:hidden;
width:610px;
margin: 50px auto;
text-align: center;
}
#comic panel{
display: inline-block;
width: 250px;
height: 250px;
/*border: 2px solid #ccc;*/
margin: 5px;
position: relative;
overflow: hidden;
transition: opacity 0.5s ease-in-out;
}
#comic panel[fadeInOn]{
opacity: 0;
}
#comic panel pic{
display: block;
position: absolute;
top:0;
left:0;
width: 100%;
height: 100%;
/*background: #bada55;*/
background: #ccc;
}
#comic panel words{
display: block;
position: absolute;
top:0;
left:0;
background: #fff;
/*text-align: left;*/
padding: 15px;
}
#comic panel words[no-bg]{
background: none;
}
#comic panel sim{
display: block;
position: absolute;
}
#comic panel sim > iframe{
border: none;
width: 100%;
height: 100%;
position: absolute;
top:0; left:0;
}
#comic panel sim > label{
display:none;
}
/**********/
/* LABELS */
/**********/
#labels{
display: none;
}

460
index.html Normal file
View file

@ -0,0 +1,460 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>An Interactive Comic</title>
<link rel="stylesheet" type="text/css" href="css/comic.css"/>
</head>
<body>
<div id="comic">
<!-- - - - - - - - - - - - -->
<!-- THE SCIENCE of SRS - - -->
<!-- - - - - - - - - - - - -->
<panel w=600 h=90>
<words w=600 x=-15 no-bg>
(<b>This is a work-in-progress!</b>
Please don't share yet.
<br>
Let me know your honest feedback, thanks!)
</words>
</panel>
<!-- Part I: Decay -->
<panel w=500 h=450>
<pic src="pics/sci0.png" sx=0 sy=0></pic>
<words x=10 y=10 w=400 h=60>
In 1885, Hermann Ebbinghaus performed an act of scientific masochism.
</words>
<words x=30 y=320 w=430 h=90>
The German psychologist memorized <i>thousands</i> of nonsense words,
recorded how much he forgot over time, and discovered...
</words>
</panel>
<panel w=400 h=500>
<pic src="pics/sci0.png" sx=500 sy=0></pic>
<words x=60 y=10 w=250>
<b>THE FORGETTING CURVE</b>
</words>
<words x=10 y=310 w=350>
He found that you forget most of what you learn in the first 24 hours,
then if you dont practice recall your remaining memories decay exponentially.
</words>
</panel>
<panel w=500 h=450>
<pic src="pics/sci0.png" sx=900 sy=0></pic>
<words x=10 y=10 w=400 h=60>
Since then, Ebbinghauss findings have been replicated again and again
</words>
<words x=60 y=350 w=400 h=60>
and grew into a whole new scientific field of memory!
</words>
</panel>
<panel w=600 h=80>
<words w=600 x=-15 no-bg>
Heres a playable simulation of the Forgetting Curve.
<br>
<b>Change the rate of “memory decay”. What happens?</b>
</words>
</panel>
<panel w=600 h=370>
<sim x=0 y=0 w=600 h=370 src="sims/ebbinghaus?mode=0"></sim>
</panel>
<!-- Part II: Reminder -->
<panel w=600 h=90>
<words w=600 x=-15 no-bg>
As you can see, the less the decay, the flatter the curve
that is, the longer the memory lasts.
</words>
</panel>
<panel w=400 h=400>
<pic src="pics/sci0.png" sx=0 sy=500></pic>
<words x=10 y=10 w=300>
How fast a persons memory decays depends on the person and the memory...
</words>
</panel>
<panel w=400 h=400>
<pic src="pics/sci0.png" sx=400 sy=500></pic>
<words x=10 y=10 w=300>
But, in general, a memorys “rate of decay” slows down each time you <b>actively recall</b> it.
</words>
</panel>
<panel w=400 h=400>
<pic src="pics/sci0.png" sx=800 sy=500></pic>
<words x=10 y=10 w=300>
(although, when you stop practicing, it still decays.)
</words>
</panel>
<panel w=600 h=120>
<words w=600 x=-15 no-bg>
Heres the simulation again, with a single active recall session.
<br>
(grey line: what memory would've been <i>without</i> the recall)
<br>
<b>Change the timing of the recall. What happens?</b>
</words>
</panel>
<panel w=600 h=400>
<sim x=0 y=0 w=600 h=400 src="sims/ebbinghaus?mode=1"></sim>
</panel>
<!-- Part III: Desirable Difficulty -->
<panel w=600 h=90>
<words w=600 x=-15 no-bg>
A single recall boosts memory for a bit... but in the long run,
due to exponential decay of memory, a single recall changes nothing.
</words>
</panel>
<panel w=450 h=400>
<pic src="pics/sci1.png" sx=0 sy=0></pic>
<words x=10 y=10 w=390>
Is there a better way to learn?
There is! The trick to remembering...
</words>
<words x=210 y=330 w=200>
...<i>is to forget.</i>
</words>
</panel>
<panel w=500 h=300>
<pic src="pics/sci1.png" sx=450 sy=0></pic>
<words x=250 y=20 w=200>
To understand this, think about training your muscles.
Youll gain nothing with a weight thats too easy...
</words>
</panel>
<panel w=500 h=300>
<pic src="pics/sci1.png" sx=450 sy=300></pic>
<words x=250 y=20 w=200>
...nor one thats too hard.
</words>
</panel>
<panel w=350 h=350>
<pic src="pics/sci1.png" sx=950 sy=0></pic>
<words x=10 y=10 w=300>
The sames true of training your brain.
You need <b>desirable difficulty</b>: the sweet spot of just-hard-enough.
</words>
</panel>
<panel w=450 h=400>
<pic src="pics/sci1.png" sx=0 sy=400></pic>
<words x=10 y=10 w=360>
Therefore: to best learn something, you need to recall it...
</words>
<words x=60 y=330 w=350>
...<i>just as youre about to forget it.</i>
</words>
</panel>
<panel w=600 h=120>
<words w=600 x=-15 no-bg>
Same simulation as before, but now it shows the <span style="background:#ffe866">sweet spot</span>
where youve forgotten <i>just a little bit.</i>
<br>
<b>Change the timing of the recall. What happens now?</b>
</words>
</panel>
<panel w=600 h=400>
<sim x=0 y=0 w=600 h=400 src="sims/ebbinghaus?mode=2"></sim>
</panel>
<!-- Part IV: Bigger and Bigger spaces -->
<panel w=600 h=90>
<words w=600 x=-15 no-bg>
See? If you time a <i>single</i> recall so that it's in the sweet spot,
you can slow down the decay!
Now, what about <i>multiple</i> recalls?
</words>
</panel>
<panel w=500 h=450>
<pic src="pics/sci2.png" sx=0 sy=0></pic>
<words x=10 y=10 w=430>
Lets say youre
<span style="text-decoration:line-through;">lazy</span>
time-efficient, so youre only doing 4 recall sessions.
</words>
<words x=30 y=350 w=430>
Question:
<i>whats the best way to spread out your recalls?</i>
</words>
</panel>
<panel w=500 h=350>
<pic src="pics/sci2.png" sx=500 sy=0></pic>
<words x=10 y=10 w=190>
Should you have evenly spaced gaps?
Gaps of increasing length?
Gaps of decreasing length?
Or make it unpredictable, to keep you on your toes?
</words>
</panel>
<panel w=400 h=90>
<words h=90>
<b>Give it your best guess</b>,
then when youre ready, <b>flip the card over &darr;</b>
</words>
</panel>
<panel w=600 h=300 bg="#e0e0e0">
<sim x=80 y=0 w=440 h=300 src="sims/singlecard?card=guessgap"></sim>
</panel>
<panel fadeInOn="flip_guessgap" w=600 h=120>
<words w=600 x=-15 y=0 no-bg>
Which is very counter-intuitive!
You can prove to yourself this is true, by playing with the sim below.
<b>
Get all recalls into the <span style="background:#ffe866">sweet spot</span>.
What spacing do you get?
</b>
</words>
</panel>
<panel w=600 h=470>
<sim x=0 y=0 w=600 h=470 src="sims/ebbinghaus?mode=3"></sim>
</panel>
<!-- Part V: Sandbox -->
<panel w=600 h=120>
<words w=600 x=-15 no-bg>
(To prove this isn't a fluke,
heres a sim where you can change
the initial memory decay &amp; sweet spot.
Note how, in all but the extreme cases,
the best schedule is still “increasing gaps”!)
</words>
</panel>
<panel w=600 h=520>
<sim x=0 y=0 w=600 h=520 src="sims/ebbinghaus?mode=4"></sim>
</panel>
<!-- Part VI: And now, in practice... -->
<panel w=350 h=500>
<pic src="pics/sci2.png" sx=0 sy=450></pic>
<words x=10 y=10 w=300>
Why <i>must</i> the gaps increase?
Because: each time you do a recall at the sweet spot of forgetting,
the memorys decay slows down...
</words>
<words x=10 y=400 w=300>
...meaning itll take <i>longer</i>
to hit the sweet spot next time!
</words>
</panel>
<panel w=500 h=500>
<pic src="pics/sci2.png" sx=350 sy=450></pic>
<words x=10 y=10 w=400>
But you know whats sweeter?
This also means if you time your recalls just right...
</words>
<words x=60 y=400 w=400>
...you can easily keep <i>any number</i> of things in your long-term memory,
<i>FOREVER.</i>
</words>
</panel>
<panel w=250 h=250>
<pic src="pics/sci2.png" sx=850 sy=450></pic>
</panel>
<panel w=600 h=90>
<words w=600 x=-15 no-bg>
And speaking of doing active recall in order to learn,
let's do some active recall on what we just learnt:
</words>
</panel>
<panel w=600 h=400 bg="#e0e0e0">
<sim x=0 y=0 w=600 h=400 src="sims/multicard/?cards=sci_a,sci_b,sci_c"></sim>
</panel>
<panel w=400 h=400>
<pic src="pics/sci2.png" sx=0 sy=950></pic>
<words x=50 y=20 w=300 bg=none>
Anyway, this all sounds great,
but finding the optimal schedule must be impossible, right?
</words>
</panel>
<panel w=400 h=400>
<pic src="pics/sci2.png" sx=400 sy=950></pic>
<words x=50 y=30 w=300 bg=none>
<i>Au contraire!</i>
Its so simple, you can even create your own automatic scheduler...
</words>
</panel>
<panel w=400 h=400>
<pic src="pics/sci2.png" sx=800 sy=950></pic>
<words x=30 y=30 w=200 bg=none>
...using a <i>shoebox.</i>
</words>
</panel>
<panel w=600 h=500>
<words w=600 x=-15 no-bg>
<b>[END OF PROTOTYPE]</b>
<br><br>
Sorry for the cliffhanger!
The rest of this comic would show you how to make a <b>Leitner Box</b>,
tell you about other digital <b>spaced repetition systems</b> like <b>Anki</b>,
and finally, help you get started using spaced repetition <i>today!</i>
<br><br>
(And every once in a while, it'll use flashcards to get you
to actively recall what you just learnt. I'll use spaced repetition
to teach you <i>about</i> spaced repetition!)
<br><br>
Anyway, please let me know your <i>honest</i> feedback so far!
Early feedback helps me a lot. Many thanks in advance!
<br><br>
&lt;3,
<br>~ Nicky
</words>
</panel>
</div>
<!-- SIMULATION LABELS -->
<div id="labels">
<!-- Ebbinghaus -->
<span id="ebbinghaus_y_axis">
memory &rarr;
</span>
<span id="ebbinghaus_x_axis">
time &rarr;
</span>
<span id="ebbinghaus_decay">
decay:
</span>
<span id="ebbinghaus_forgetting">
sweet spot:
</span>
<span id="ebbinghaus_recalls">
timing of recall(s):
</span>
<span id="ebbinghaus_auto">
auto-optimize!
</span>
<!-- Flashcards -->
<span id="flashcard_guessgap_front">
<div class="fcard_center" style="height:2.5em">
the best way to space out your recalls is...
</div>
</span>
<span id="flashcard_guessgap_back">
<div class="fcard_bg" src="pics/fcards0.png" sx=0 sy=0></div>
<div class="fcard_center" style="height:2.5em">
...with <i>increasing</i> gaps!
</div>
</span>
<!-- Flashcards -->
<span id="flashcard_sci_a_front">
<div class="fcard_center" style="height:2.5em">
The discoverer of the Forgetting Curve was...
</div>
</span>
<span id="flashcard_sci_a_back">
<div class="fcard_bg" src="pics/fcards0.png" sx=400 sy=0></div>
<div style="position: absolute; width: 250px; top: 60px; right: 0; line-height: 1.1em;">
Hermann Ebbinghaus
</div>
</span>
<span id="flashcard_sci_b_front">
<div class="fcard_center" style="height:3.4em">
The Forgetting Curve (<i>without</i> any recalls) looks like...
</div>
</span>
<span id="flashcard_sci_b_back">
<div class="fcard_bg" src="pics/fcards0.png" sx=0 sy=240></div>
<div style="position: absolute; width: 280px; top: 70px; right: 20px; font-size:20px; line-height: 1.1em;">
(note: it decays quickly, then slowly "exponential decay")
</div>
</span>
<span id="flashcard_sci_c_front">
<div class="fcard_center" style="height:3.4em">
The Forgetting Curve (<i>with</i> optimally-spaced recalls) looks like...
</div>
</span>
<span id="flashcard_sci_c_back">
<div class="fcard_bg" src="pics/fcards0.png" sx=400 sy=240></div>
<div style="position: absolute; width: 360px; top: 120px; left: 20px; font-size:20px; line-height: 1.1em;">
(note: the gaps between recalls <i>increase</i> in length)
</div>
</span>
<!-- Multi Card Labels -->
<span id="multicard_q">
try to recall &uarr;
then flip ↻
</span>
<span id="multicard_cards_left">
(cards left: [N])
</span>
<span id="multicard_a">
did you remember this?
</span>
<span id="multicard_no">
nah, try again
</span>
<span id="multicard_yes">
yup, onwards!
</span>
<span id="multicard_done">
done for now! keep scrolling
<br>
&darr;
</span>
</div>
</body>
</html>
<script src="js/minpubsub.src.js"></script>
<script src="js/comic.js"></script>

92
js/comic.js Normal file
View file

@ -0,0 +1,92 @@
// The poor man's jQuery
function $(query){
return document.querySelector(query);
}
function $all(query){
return [].slice.call(document.querySelectorAll(query));
}
window.onload = function(){
var panels = $all("panel");
var pics = $all("pic");
var sims = $all("sim");
var words = $all("words");
// Adjust positions & dimensions of all the things
var boxes = panels.concat(pics).concat(words).concat(sims);
boxes.forEach(function(b){
var s = b.style;
var val;
if(val = b.getAttribute("x")) s.left = val+"px";
if(val = b.getAttribute("y")) s.top = val+"px";
if(val = b.getAttribute("w")) s.width = val+"px";
if(val = b.getAttribute("h")) s.height = val+"px";
});
// Pics have image (and maybe crop it?)
pics.forEach(function(p){
var s = p.style;
var val;
if(val = p.getAttribute("src")){
s.backgroundImage = "url("+val+")";
var x = p.getAttribute("sx") || 0;
var y = p.getAttribute("sy") || 0;
s.backgroundPosition = (-x)+"px "+(-y)+"px";
var w = p.getBoundingClientRect().width;
s.backgroundSize = Math.round((3000/w)*50)+"%";
}
});
// Sims have iframes in them. (Pass in the labels!)
sims.forEach(function(sim){
// Create & append iframe
var iframe = document.createElement("iframe");
iframe.src = sim.getAttribute("src");
iframe.scrolling = "no";
sim.appendChild(iframe);
});
// Words... no bg? And, fontsize?
words.forEach(function(word){
var s = word.style;
var val;
if(val = word.getAttribute("bg")) s.background = val;
if(val = word.getAttribute("fontsize")) s.fontSize = s.lineHeight = val+"px";
});
// Panels... Any MESSAGES?
panels.forEach(function(panel){
var msg;
// Fade in!
if(msg = panel.getAttribute("fadeInOn")){
subscribe(msg, function(){
panel.style.opacity = 1;
});
}
// BG?
var s = panel.style;
var val;
if(val = panel.getAttribute("bg")) s.background = val;
});
};
window.getLabel = function(name){
return $("#"+name).innerHTML;
}
window.broadcastMessage = function(message){
publish(message);
};

96
js/minpubsub.src.js Normal file
View file

@ -0,0 +1,96 @@
/*!
* MinPubSub
* Copyright(c) 2011 Daniel Lamb <daniellmb.com>
* MIT Licensed
*/
window.c_ = {}; // NICKY - UNIT TESTING
(function (context) {
var MinPubSub = {};
// the topic/subscription hash
var cache = context.c_ || {}; //check for 'c_' cache for unit testing
MinPubSub.publish = function ( /* String */ topic, /* Array? */ args) {
// summary:
// Publish some data on a named topic.
// topic: String
// The channel to publish on
// args: Array?
// The data to publish. Each array item is converted into an ordered
// arguments on the subscribed functions.
//
// example:
// Publish stuff on '/some/topic'. Anything subscribed will be called
// with a function signature like: function(a,b,c){ ... }
//
// publish('/some/topic', ['a','b','c']);
var subs = cache[topic],
len = subs ? subs.length : 0;
//can change loop or reverse array if the order matters
while (len--) {
subs[len].apply(context, args || []);
}
};
MinPubSub.subscribe = function ( /* String */ topic, /* Function */ callback) {
// summary:
// Register a callback on a named topic.
// topic: String
// The channel to subscribe to
// callback: Function
// The handler event. Anytime something is publish'ed on a
// subscribed channel, the callback will be called with the
// published array as ordered arguments.
//
// returns: Array
// A handle which can be used to unsubscribe this particular subscription.
//
// example:
// subscribe('/some/topic', function(a, b, c){ /* handle data */ });
if (!cache[topic]) {
cache[topic] = [];
}
cache[topic].push(callback);
return [topic, callback]; // Array
};
MinPubSub.unsubscribe = function ( /* Array */ handle, /* Function? */ callback) {
// summary:
// Disconnect a subscribed function for a topic.
// handle: Array
// The return value from a subscribe call.
// example:
// var handle = subscribe('/some/topic', function(){});
// unsubscribe(handle);
var subs = cache[callback ? handle : handle[0]],
callback = callback || handle[1],
len = subs ? subs.length : 0;
while (len--) {
if (subs[len] === callback) {
subs.splice(len, 1);
}
}
};
// UMD definition to allow for CommonJS, AMD and legacy window
if (typeof module === 'object' && module.exports) {
// CommonJS, just export
module.exports = exports = MinPubSub;
} else if (typeof define === 'function' && define.amd) {
// AMD support
define(function () {
return MinPubSub;
});
} else if (typeof context === 'object') {
// If no AMD and we are in the browser, attach to window
context.publish = MinPubSub.publish;
context.subscribe = MinPubSub.subscribe;
context.unsubscribe = MinPubSub.unsubscribe;
}
})(this.window);

BIN
pics/cardboard.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

BIN
pics/fcards0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

BIN
pics/sci0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 KiB

BIN
pics/sci1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

BIN
pics/sci2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB

View file

@ -0,0 +1,67 @@
/* FONT FACE */
@font-face {
font-family: "PatrickHand";
font-style: normal;
font-weight: 400;
src: url(../../css/PatrickHand-Regular.ttf) format('truetype');
}
body{
margin: 0;
font-family: "PatrickHand", Helvetica, Arial;
color: #000;
padding: 0 10px;
background: #fff;
letter-spacing: 1px;
}
#content{
background: #fff;
width: 600px;
overflow: hidden;
position: absolute;
top: 0;
left: 0;
}
#container{
width: 500px;
margin: 30px auto;
}
#graph{
border-left: 2px solid black;
border-bottom: 2px solid black;
}
#y_axis, #x_axis{
font-size: 18px;
position: absolute;
color: #000;
}
#y_axis{
position: absolute;
transform: rotate(-90deg);
left: -67px;
top: 114px;
text-align: right;
width: 200px;
}
#x_axis{
width: 200px;
left: 358px;
top: 282px;
text-align: right;
}
#ui{
margin-top: 25px;
font-size: 25px;
line-height: 1em;
}
input[fullw]{
width:100%;
}
#default_labels{
display:none;
}

View file

@ -0,0 +1,398 @@
window.MODE = -1;
window.onload = function(){
// Get Mode
window.MODE = parseInt( _getQueryVariable("mode") );
// Init labels
$("#y_axis").innerHTML = _getLabel("ebbinghaus_y_axis");
$("#x_axis").innerHTML = _getLabel("ebbinghaus_x_axis");
// Initialize all the things!
switch(MODE){
// Just decay
case 0:
sim_params.push(_sliders.d);
break;
// ONE retrieval
case 1:
recall_params.push(_sliders.r1);
break;
// ONE retrieval, with optimal learning
case 2:
PARAMS.optimal = 0.75;
recall_params.push(_sliders.r1);
break;
// MULTI retrievals, with optimal learning
case 3:
PARAMS.optimal = 0.75;
recall_params.push(_sliders.r1);
recall_params.push(_sliders.r2);
recall_params.push(_sliders.r3);
recall_params.push(_sliders.r4);
break;
// FULL SANDBOX
case 4:
sim_params.push(_sliders.d);
sim_params.push(_sliders.o);
recall_params.push(_sliders.r1);
recall_params.push(_sliders.r2);
recall_params.push(_sliders.r3);
recall_params.push(_sliders.r4);
break;
}
sim_params.concat(recall_params).forEach(_createParamSlider);
// Add UI
switch(MODE){
// Just decay
case 0:
_appendSpan("ebbinghaus_decay");
_appendSlider("init_decay");
break;
// ONE retrieval
case 1:
_appendSpan("ebbinghaus_recalls");
_appendSlider("recall_1");
break;
// ONE retrieval, with optimal learning
case 2:
_appendSpan("ebbinghaus_recalls");
_appendSlider("recall_1");
break;
// MULTI retrievals, with optimal learning
case 3:
_appendSpan("ebbinghaus_recalls");
_appendSlider("recall_1");
_appendSlider("recall_2");
_appendSlider("recall_3");
_appendSlider("recall_4");
break;
// FULL SANDBOX
case 4:
_appendSpan("ebbinghaus_decay");
_appendSlider("init_decay");
_appendSpan("ebbinghaus_forgetting");
_appendSlider("optimal");
_appendBr();
_appendBr();
_appendSpan("ebbinghaus_recalls");
_appendButton("ebbinghaus_auto",_AUTO_OPTIMIZE);
_appendSlider("recall_1");
_appendSlider("recall_2");
_appendSlider("recall_3");
_appendSlider("recall_4");
break;
}
// Update
window.PARAMS_CHANGED = true;
update();
};
////////////////////////////////////////
////////////////////////////////////////
////////////////////////////////////////
// The most fudge-y function in existence
var _AUTO_OPTIMIZE = function(){
// When t hits the sweet spot (k)...
// k = A*e^(-Bt)
// Therefore:
// t = (ln(A) - ln(k))/B
// Dunno what A & B are. Fudge it.
var A = 1; // coz, at 0, it's 1. Duh.
var k = PARAMS.optimal;
var B = 102 * PARAMS.init_decay * MAGIC_CONSTANT; // FUDGE IT.
var timing = (Math.log(A) - Math.log(k))/B;
// Set first one!
_sliderUI.recall_1.value = timing;
_sliderUI.recall_1.oninput();
// Multiply by fudged values for the rest.
timing *= 2.92;
_sliderUI.recall_2.value = timing;
_sliderUI.recall_2.oninput();
timing *= 2.23;
_sliderUI.recall_3.value = timing;
_sliderUI.recall_3.oninput();
timing *= 2.01;
_sliderUI.recall_4.value = timing;
_sliderUI.recall_4.oninput();
};
////////////////////////////////////////
////////////////////////////////////////
////////////////////////////////////////