beta
This commit is contained in:
commit
4670ff8e93
|
@ -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 & 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>
|
Binary file not shown.
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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 don’t 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, Ebbinghaus’s 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>
|
||||||
|
Here’s 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 person’s 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 memory’s “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>
|
||||||
|
Here’s 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.
|
||||||
|
You’ll gain nothing with a weight that’s 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 that’s 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 same’s 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 you’re 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 you’ve 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>
|
||||||
|
Let’s say you’re
|
||||||
|
<span style="text-decoration:line-through;">lazy</span>
|
||||||
|
time-efficient, so you’re only doing 4 recall sessions.
|
||||||
|
</words>
|
||||||
|
<words x=30 y=350 w=430>
|
||||||
|
Question:
|
||||||
|
<i>what’s 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 you’re ready, <b>flip the card over ↓</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,
|
||||||
|
here’s a sim where you can change
|
||||||
|
the initial memory decay & 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 memory’s decay slows down...
|
||||||
|
</words>
|
||||||
|
<words x=10 y=400 w=300>
|
||||||
|
...meaning it’ll 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 what’s 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>
|
||||||
|
It’s 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>
|
||||||
|
|
||||||
|
<3,
|
||||||
|
<br>~ Nicky
|
||||||
|
|
||||||
|
</words>
|
||||||
|
</panel>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- SIMULATION LABELS -->
|
||||||
|
<div id="labels">
|
||||||
|
|
||||||
|
<!-- Ebbinghaus -->
|
||||||
|
<span id="ebbinghaus_y_axis">
|
||||||
|
memory →
|
||||||
|
</span>
|
||||||
|
<span id="ebbinghaus_x_axis">
|
||||||
|
time →
|
||||||
|
</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 ↑
|
||||||
|
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>
|
||||||
|
↓
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
||||||
|
<script src="js/minpubsub.src.js"></script>
|
||||||
|
<script src="js/comic.js"></script>
|
|
@ -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);
|
||||||
|
};
|
|
@ -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);
|
Binary file not shown.
After Width: | Height: | Size: 1.9 MiB |
Binary file not shown.
After Width: | Height: | Size: 141 KiB |
Binary file not shown.
After Width: | Height: | Size: 362 KiB |
Binary file not shown.
After Width: | Height: | Size: 196 KiB |
Binary file not shown.
After Width: | Height: | Size: 309 KiB |
|
@ -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;
|
||||||
|
}
|
|
@ -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();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////
|
||||||
|
////////////////////////////////////////
|
||||||
|
////////////////////////////////////////
|
||||||
|
|
||||||
|
var canvas = document.getElementById("graph");
|
||||||
|
var ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
// Params & their sliders
|
||||||
|
var PARAMS = {};
|
||||||
|
var sim_params = [];
|
||||||
|
var recall_params = [];
|
||||||
|
var _sliders = {
|
||||||
|
d: {name:"init_decay", min:0, max:1, step:0.01, value:0.5},
|
||||||
|
o: {name:"optimal", min:0, max:1, step:0.01, value:0.75},
|
||||||
|
r1: {name:"recall_1", min:0, max:10, step:0.01, value:2.0, fullw:true},
|
||||||
|
r2: {name:"recall_2", min:0, max:10, step:0.01, value:4.0, fullw:true},
|
||||||
|
r3: {name:"recall_3", min:0, max:10, step:0.01, value:6.0, fullw:true},
|
||||||
|
r4: {name:"recall_4", min:0, max:10, step:0.01, value:8.0, fullw:true},
|
||||||
|
//r5: {name:"recall_5", min:0, max:10, step:0.01, value:2.5, fullw:true}
|
||||||
|
};
|
||||||
|
var _sliderUI = {};
|
||||||
|
var _appendBr = function(){
|
||||||
|
$("#ui").appendChild(document.createElement("br"));
|
||||||
|
};
|
||||||
|
var _appendSpan = function(name){
|
||||||
|
var label = _getLabel(name);
|
||||||
|
var span = document.createElement("span");
|
||||||
|
span.innerHTML = label;
|
||||||
|
$("#ui").appendChild(span);
|
||||||
|
};
|
||||||
|
var _appendSlider = function(name){
|
||||||
|
$("#ui").appendChild(_sliderUI[name]);
|
||||||
|
}
|
||||||
|
var _appendButton = function(name, onclick){
|
||||||
|
var label = _getLabel(name);
|
||||||
|
var button = document.createElement("button");
|
||||||
|
button.innerHTML = label;
|
||||||
|
button.onclick = onclick;
|
||||||
|
$("#ui").appendChild(button);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.PARAMS_CHANGED = false;
|
||||||
|
var _createParamSlider = function(config){
|
||||||
|
|
||||||
|
// Make DOM
|
||||||
|
var slider = document.createElement("input");
|
||||||
|
slider.type = "range";
|
||||||
|
slider.min = config.min;
|
||||||
|
slider.max = config.max;
|
||||||
|
slider.step = config.step;
|
||||||
|
slider.value = config.value;
|
||||||
|
if(config.fullw) slider.setAttribute("fullw","yes");
|
||||||
|
|
||||||
|
// Gimme DOM
|
||||||
|
_sliderUI[config.name] = slider;
|
||||||
|
|
||||||
|
// Sync it
|
||||||
|
var _onSliderUpdate = function(){
|
||||||
|
PARAMS[config.name] = parseFloat(slider.value);
|
||||||
|
PARAMS_CHANGED = true;
|
||||||
|
};
|
||||||
|
slider.oninput = _onSliderUpdate;
|
||||||
|
_onSliderUpdate();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update
|
||||||
|
var MAGIC_CONSTANT = 0.013815; // Through pure brute force, don't care.
|
||||||
|
var ERROR = 0.00001;
|
||||||
|
var OPTIMAL_RANGE = 0.10; //0.05;
|
||||||
|
function update(){
|
||||||
|
|
||||||
|
// Don't re-draw unnecessarily!
|
||||||
|
if(window.PARAMS_CHANGED){
|
||||||
|
|
||||||
|
window.PARAMS_CHANGED = false;
|
||||||
|
|
||||||
|
// Clear & Retina
|
||||||
|
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
|
||||||
|
ctx.save();
|
||||||
|
ctx.scale(2,2);
|
||||||
|
|
||||||
|
// Memory strength over 1000 iterations...
|
||||||
|
var memory = 1; // full strength at first!
|
||||||
|
var decay = PARAMS.init_decay!==undefined ? PARAMS.init_decay : 0.5; // init decay...
|
||||||
|
var optimal = PARAMS.optimal!==undefined ? PARAMS.optimal : 999;
|
||||||
|
var curves = [
|
||||||
|
{ start:0, memory:1, decay:decay, line:[], cut:-1 } // original line
|
||||||
|
];
|
||||||
|
|
||||||
|
//debugger;
|
||||||
|
|
||||||
|
var ALREADY_USED_THESE_CUTS = {};
|
||||||
|
|
||||||
|
// For each timestep, and all curves...
|
||||||
|
for(var t=0; t<=10; t+=0.01){
|
||||||
|
for(var c=0; c<curves.length; c++){
|
||||||
|
|
||||||
|
var curve = curves[c];
|
||||||
|
|
||||||
|
// Record!
|
||||||
|
curve.line.push({
|
||||||
|
t: t,
|
||||||
|
m: curve.memory
|
||||||
|
});
|
||||||
|
|
||||||
|
// Decay a bit...
|
||||||
|
curve.memory -= curve.memory * curve.decay * MAGIC_CONSTANT;
|
||||||
|
|
||||||
|
// If not already cut...
|
||||||
|
if(curve.cut<0){
|
||||||
|
|
||||||
|
// If this is in a recall session, create ONE NEW CURVE
|
||||||
|
// Find THE FIRST AND ONLY ONE cut.
|
||||||
|
for(var cutIndex=0; cutIndex<recall_params.length; cutIndex++){
|
||||||
|
|
||||||
|
var config = recall_params[cutIndex];
|
||||||
|
|
||||||
|
if(ALREADY_USED_THESE_CUTS[config.name]) continue; // DON'T RE-USE CUT
|
||||||
|
|
||||||
|
var recall_t = PARAMS[config.name];
|
||||||
|
if(Math.abs(t-recall_t)<ERROR){ // floating point
|
||||||
|
|
||||||
|
// If memory is near optimal range, HALVE decay.
|
||||||
|
if(Math.abs(curve.memory-optimal)<OPTIMAL_RANGE){
|
||||||
|
//decay *= 0.5;
|
||||||
|
// Continuous?
|
||||||
|
var distanceFromOptimal = Math.abs(curve.memory-optimal)/OPTIMAL_RANGE;
|
||||||
|
var decayMultiplier = (0.5+distanceFromOptimal*0.5); // (0,1) => (0.5,1.0)
|
||||||
|
decay *= decayMultiplier;
|
||||||
|
}else{
|
||||||
|
if(MODE===1){
|
||||||
|
decay *= 0.9; // helps a little bit ANYWAY?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cut THIS curve.
|
||||||
|
curve.cut = t;
|
||||||
|
|
||||||
|
// Create new curve!
|
||||||
|
curves.push({
|
||||||
|
start: t,
|
||||||
|
memory: 1, // boost memory to top!
|
||||||
|
decay: decay,
|
||||||
|
line: [],
|
||||||
|
cut: -1
|
||||||
|
});
|
||||||
|
|
||||||
|
// WE USED THIS CUT
|
||||||
|
ALREADY_USED_THESE_CUTS[config.name] = true;
|
||||||
|
|
||||||
|
// And, use NO MORE CUTS
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// DRAW.
|
||||||
|
|
||||||
|
// Days
|
||||||
|
/*ctx.strokeStyle = "#ddd";
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.beginPath();
|
||||||
|
for(var t=0; t<10; t++){
|
||||||
|
var from = _project(t,0);
|
||||||
|
var to = _project(t,1);
|
||||||
|
ctx.moveTo(from.x,from.y);
|
||||||
|
ctx.lineTo(to.x,to.y);
|
||||||
|
}
|
||||||
|
ctx.stroke();*/
|
||||||
|
|
||||||
|
// Ideal Forgetting
|
||||||
|
//ctx.fillStyle = "#FFD700";
|
||||||
|
var tl = _project(0,optimal+OPTIMAL_RANGE);
|
||||||
|
var br = _project(10,optimal-OPTIMAL_RANGE);
|
||||||
|
var gradient = ctx.createLinearGradient(0, tl.y, 0, br.y);
|
||||||
|
gradient.addColorStop(0.0, "hsl(51, 100%, 50%, 0)");
|
||||||
|
gradient.addColorStop(0.5, "hsl(51, 100%, 50%, 0.5)");
|
||||||
|
gradient.addColorStop(1.0, "hsl(51, 100%, 50%, 0)");
|
||||||
|
ctx.fillStyle = gradient;
|
||||||
|
ctx.fillRect(tl.x, tl.y, br.x-tl.x, br.y-tl.y);
|
||||||
|
|
||||||
|
// DRAW THE POTENTIAL CURVES
|
||||||
|
ctx.lineJoin = ctx.lineCap = "round";
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.strokeStyle = "rgba(0,0,0,0.2)";
|
||||||
|
for(var c=0; c<curves.length; c++){
|
||||||
|
var curve = curves[c];
|
||||||
|
if(curve.cut>=0){ // only draw if line HAS been cut
|
||||||
|
var imCut = false;
|
||||||
|
for(var i=0; i<curve.line.length; i++){
|
||||||
|
var point = curve.line[i];
|
||||||
|
p = _project(point.t, point.m);
|
||||||
|
if(point.t>=curve.cut && !imCut){ // CUT. Start drawing!
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(p.x,p.y);
|
||||||
|
imCut = true;
|
||||||
|
}else{
|
||||||
|
ctx.lineTo(p.x,p.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DRAW THE MAIN THICK CURVES
|
||||||
|
ctx.lineWidth = 5;
|
||||||
|
// For each curve, draw until cut.
|
||||||
|
for(var c=0; c<curves.length; c++){
|
||||||
|
|
||||||
|
var curve = curves[c];
|
||||||
|
|
||||||
|
// Redness for decay
|
||||||
|
var d = Math.sqrt(Math.sqrt(curve.decay));
|
||||||
|
var saturation = Math.round(d*100); // (0,1) => (0,100)
|
||||||
|
var lightness = Math.round(d*70); // (0,1) => (0,60)
|
||||||
|
ctx.strokeStyle = "hsl(0,"+saturation+"%,"+lightness+"%)";
|
||||||
|
|
||||||
|
var imCut = false;
|
||||||
|
for(var i=0; i<curve.line.length; i++){
|
||||||
|
var point = curve.line[i];
|
||||||
|
if(curve.cut<0 || point.t<=curve.cut){ // CUT
|
||||||
|
p = _project(point.t, point.m);
|
||||||
|
if(i==0){
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(p.x,p.y);
|
||||||
|
}else{
|
||||||
|
ctx.lineTo(p.x,p.y);
|
||||||
|
}
|
||||||
|
}else if(!imCut){
|
||||||
|
p = _project(point.t, 1);
|
||||||
|
ctx.lineTo(p.x,p.y);
|
||||||
|
imCut = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// And, again...
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(update);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var _project = function(x,y){
|
||||||
|
return {
|
||||||
|
x: (x/10.05) * (canvas.width/2), // retina (with lil' buffer)
|
||||||
|
y: (1-(y*0.98)) * (canvas.height/2) // retina (with lil' buffer)
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,45 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>The Forgetting Curve</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="ebbinghaus.css"/>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="content">
|
||||||
|
<div id="container">
|
||||||
|
<canvas id="graph" width="1000" height="500" style="width:500px; height:250px;"></canvas>
|
||||||
|
<div id="y_axis"></div>
|
||||||
|
<div id="x_axis"></div>
|
||||||
|
<div id="ui"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="default_labels">
|
||||||
|
<span id="ebbinghaus_y_axis">
|
||||||
|
*memory →
|
||||||
|
</span>
|
||||||
|
<span id="ebbinghaus_x_axis">
|
||||||
|
*time →
|
||||||
|
</span>
|
||||||
|
<span id="ebbinghaus_decay">
|
||||||
|
*decay:
|
||||||
|
</span>
|
||||||
|
<span id="ebbinghaus_forgetting">
|
||||||
|
*optimal:
|
||||||
|
</span>
|
||||||
|
<span id="ebbinghaus_recalls">
|
||||||
|
*recalls:
|
||||||
|
</span>
|
||||||
|
<span id="ebbinghaus_auto">
|
||||||
|
*auto
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
||||||
|
<script src="../helpers.js"></script>
|
||||||
|
<script src="ebbinghaus.js"></script>
|
|
@ -0,0 +1,16 @@
|
||||||
|
.fcard_center{
|
||||||
|
max-width: 370px;
|
||||||
|
position: absolute;
|
||||||
|
margin: auto;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
line-height: 1.1em;
|
||||||
|
}
|
||||||
|
.fcard_bg{
|
||||||
|
position:absolute;
|
||||||
|
width:100%;
|
||||||
|
height:100%;
|
||||||
|
background:#ff4040;
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
// The poor man's jQuery
|
||||||
|
function $(query){
|
||||||
|
return document.querySelector(query);
|
||||||
|
}
|
||||||
|
function $all(query){
|
||||||
|
return [].slice.call(document.querySelectorAll(query));
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getQueryVariable(name){
|
||||||
|
var query = window.location.search.substring(1);
|
||||||
|
var vars = query.split("&");
|
||||||
|
for(var i=0;i<vars.length;i++){
|
||||||
|
var pair = vars[i].split("=");
|
||||||
|
if(pair[0]==name) return pair[1];
|
||||||
|
}
|
||||||
|
return(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getLabel(name){
|
||||||
|
if(window!=window.top){
|
||||||
|
return window.top.getLabel(name);
|
||||||
|
}else{
|
||||||
|
return $("#"+name).innerHTML;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _modifyFlashCard(fcard){
|
||||||
|
|
||||||
|
var bg = fcard.querySelector(".fcard_bg");
|
||||||
|
if(bg){
|
||||||
|
var src = bg.getAttribute("src");
|
||||||
|
bg.style.backgroundImage = "url(../../"+src+")";
|
||||||
|
var x = bg.getAttribute("sx") || 0;
|
||||||
|
var y = bg.getAttribute("sy") || 0;
|
||||||
|
bg.style.backgroundPosition = (-x)+"px "+(-y)+"px";
|
||||||
|
var w = 400;//p.getBoundingClientRect().width;
|
||||||
|
bg.style.backgroundSize = Math.round((1600/w)*50)+"%";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>The Leitner Box</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="leitner.css"/>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="content">
|
||||||
|
<div id="container">
|
||||||
|
<canvas id="sim" width="1000" height="800" style="width:500px; height:400px;"></canvas>
|
||||||
|
<button id="next_step">next STEP</button>
|
||||||
|
<button id="next_day">next DAY</button>
|
||||||
|
<button id="next_week">next WEEK</button>
|
||||||
|
<button id="next_month">next MONTH</button>
|
||||||
|
<hr>
|
||||||
|
<span id="label_day"></span>
|
||||||
|
<br>
|
||||||
|
<span id="label_step"></span>
|
||||||
|
<br>
|
||||||
|
<span id="label_stats"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
||||||
|
<script src="../helpers.js"></script>
|
||||||
|
<script src="leitner.js"></script>
|
|
@ -0,0 +1,33 @@
|
||||||
|
/* 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: #eee;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content{
|
||||||
|
background: #fff;
|
||||||
|
width: 600px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
#container{
|
||||||
|
width: 500px;
|
||||||
|
margin: 0px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#default_labels{
|
||||||
|
display:none;
|
||||||
|
}
|
|
@ -0,0 +1,435 @@
|
||||||
|
/*******************************
|
||||||
|
|
||||||
|
// Step 1: a bunch of numbered, colored boxes (array) @done
|
||||||
|
// Step 2: a calendar @done
|
||||||
|
// Step 3: "cards" go up/down on STEP/day @done
|
||||||
|
// Step 4: ...with 5% failing @done
|
||||||
|
|
||||||
|
Next steps:
|
||||||
|
- Per DAY, now. @done
|
||||||
|
- labels for steps @done
|
||||||
|
- labels for day @done
|
||||||
|
|
||||||
|
Animation
|
||||||
|
- cards number @done
|
||||||
|
- box bounce @done
|
||||||
|
- multiple times... @done
|
||||||
|
- arrows for movement of cards
|
||||||
|
- + for adding cards
|
||||||
|
|
||||||
|
// Arrowheads @done
|
||||||
|
// TOTAL CARDS say...
|
||||||
|
|
||||||
|
CALCULATOR... nah.
|
||||||
|
|
||||||
|
********************************/
|
||||||
|
|
||||||
|
window.onload = function(){
|
||||||
|
BOXES[0] += NEW_CARDS;
|
||||||
|
_newStep();
|
||||||
|
update();
|
||||||
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////
|
||||||
|
|
||||||
|
/***************
|
||||||
|
|
||||||
|
Each day...
|
||||||
|
|
||||||
|
a. Review levels from top to bottom
|
||||||
|
(if succeed, next level)
|
||||||
|
(if fail, back to Level 1)
|
||||||
|
b. All cards in Level 1 go to Level 2
|
||||||
|
c. Add new cards to Level 1
|
||||||
|
|
||||||
|
****************/
|
||||||
|
|
||||||
|
var NEW_CARDS = 10;
|
||||||
|
var _STAGE = 0;
|
||||||
|
// 0 - new day
|
||||||
|
// 1 - reviewing
|
||||||
|
// 2 - adding
|
||||||
|
|
||||||
|
function _updateLabels(){
|
||||||
|
|
||||||
|
// Step
|
||||||
|
var html;
|
||||||
|
switch(_STAGE){
|
||||||
|
case 2:
|
||||||
|
html = _getLabel("leitner_step_new").replace("[N]",NEW_CARDS);
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
html = _getLabel("leitner_step_to_review") + QUEUE.toString();
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
html = _getLabel("leitner_step_reviewing").replace("[N]",CURRENTLY_REVIEWED+1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$("#label_step").innerHTML = html;
|
||||||
|
|
||||||
|
// Day
|
||||||
|
$("#label_day").innerHTML = _getLabel("leitner_day").replace("[N]",DAY);
|
||||||
|
|
||||||
|
// Stats
|
||||||
|
var sum = 0;
|
||||||
|
var vlt = 0;
|
||||||
|
BOXES.forEach(function(n, i){
|
||||||
|
sum += n;
|
||||||
|
if(i>=7) vlt += n; // Level 6 and up
|
||||||
|
});
|
||||||
|
html = _getLabel("leitner_step_stats").replace("[N]", sum);
|
||||||
|
if(vlt>0){
|
||||||
|
html += _getLabel("leitner_step_stats_2").replace("[N]", vlt);
|
||||||
|
}
|
||||||
|
$("#label_stats").innerHTML = html;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#next_step").onclick = function(){
|
||||||
|
_newStep();
|
||||||
|
};
|
||||||
|
function _newStep(skipLabels){
|
||||||
|
|
||||||
|
// If queue's empty, start a new day!
|
||||||
|
if(QUEUE.length==0){
|
||||||
|
if(_STAGE==1){
|
||||||
|
_STAGE = 2; // add new cards
|
||||||
|
BOXES[0] += NEW_CARDS;
|
||||||
|
|
||||||
|
// Annotate
|
||||||
|
_clearAnnotations();
|
||||||
|
_annotateAdd(NEW_CARDS);
|
||||||
|
|
||||||
|
}else{
|
||||||
|
_newDay();
|
||||||
|
_STAGE = 0; // new day!
|
||||||
|
|
||||||
|
_clearAnnotations();
|
||||||
|
|
||||||
|
}
|
||||||
|
CURRENTLY_REVIEWED = -1;
|
||||||
|
}else{
|
||||||
|
|
||||||
|
// review
|
||||||
|
_STAGE = 1;
|
||||||
|
|
||||||
|
// But if not, pop off the queue.
|
||||||
|
var reviewedLevel = QUEUE.shift();
|
||||||
|
var rIndex = reviewedLevel-1;
|
||||||
|
CURRENTLY_REVIEWED = rIndex;
|
||||||
|
|
||||||
|
// Is this Level 1?
|
||||||
|
var total, passed, failed;
|
||||||
|
total = BOXES[rIndex];
|
||||||
|
if(reviewedLevel==1){
|
||||||
|
// ALL goes to Level 2
|
||||||
|
passed = total;
|
||||||
|
failed = 0;
|
||||||
|
}else{
|
||||||
|
// 95% goes to next level
|
||||||
|
// the rest goes to ONE
|
||||||
|
passed = Math.round(total*0.95);
|
||||||
|
failed = total-passed;
|
||||||
|
}
|
||||||
|
BOXES[rIndex+1] += passed;
|
||||||
|
BOXES[0] += failed;
|
||||||
|
BOXES[rIndex] = 0;
|
||||||
|
|
||||||
|
// Annotations
|
||||||
|
_clearAnnotations();
|
||||||
|
_annotatePass(rIndex, passed);
|
||||||
|
_annotateFail(rIndex, failed);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Label
|
||||||
|
if(!skipLabels) _updateLabels();
|
||||||
|
|
||||||
|
window.REDRAW = 60;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
$("#next_day").onclick = function(){
|
||||||
|
_newDay();
|
||||||
|
_updateLabels();
|
||||||
|
};
|
||||||
|
function _newDay(skipLabels){
|
||||||
|
|
||||||
|
// Any previous stuff in queue? Finish it!
|
||||||
|
if(QUEUE.length>0){
|
||||||
|
while(QUEUE.length>0 || _STAGE==1){
|
||||||
|
_newStep(true); // until queue's done AND past "adding new cards"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increase day, add stuff to queue
|
||||||
|
DAY++;
|
||||||
|
var d = (DAY-1)%CALENDAR.length; // -1 for offset, also loop around.
|
||||||
|
QUEUE = QUEUE.concat(CALENDAR[d]); // to clone it.
|
||||||
|
|
||||||
|
// Redraw
|
||||||
|
window.REDRAW = 60;
|
||||||
|
|
||||||
|
// Label
|
||||||
|
_STAGE = 0; // new day!
|
||||||
|
_clearAnnotations();
|
||||||
|
if(!skipLabels) _updateLabels();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
function _reviewMultipleDays(days){
|
||||||
|
for(var i=0;i<days;i++) _newDay();
|
||||||
|
_updateLabels()
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#next_week").onclick = function(){
|
||||||
|
_reviewMultipleDays(7);
|
||||||
|
};
|
||||||
|
$("#next_month").onclick = function(){
|
||||||
|
_reviewMultipleDays(30);
|
||||||
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////
|
||||||
|
|
||||||
|
var canvas = document.getElementById("sim");
|
||||||
|
var ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
var COLORS = [
|
||||||
|
"#ee4035", // red
|
||||||
|
"#f37736", // orange
|
||||||
|
"#ffdb13", // yellow
|
||||||
|
"#7bc043", // green
|
||||||
|
"#0392cf", // blue
|
||||||
|
"#673888", // indigo-ish
|
||||||
|
"#ef4f91", // violet-ish
|
||||||
|
"#eeeeee" // white. VALHALLA.
|
||||||
|
];
|
||||||
|
var BOXES = [
|
||||||
|
0,0,0,0,
|
||||||
|
0,0,0,0,
|
||||||
|
];
|
||||||
|
var ANIM_CARDS = BOXES.concat(); // clone
|
||||||
|
var ANIM_BOXES = BOXES.concat(); // clone
|
||||||
|
var CURRENTLY_REVIEWED = -1;
|
||||||
|
var ANIM_EASE = 0.8;
|
||||||
|
|
||||||
|
var DAY = 0;
|
||||||
|
var QUEUE = [];
|
||||||
|
var CALENDAR = [
|
||||||
|
[2,1], [3,1], [2,1], [4,1], [2,1], [3,1], [2,1], [1],
|
||||||
|
[2,1], [3,1], [2,1], [5,1], [4,2,1], [3,1], [2,1], [1],
|
||||||
|
[2,1], [3,1], [2,1], [4,1], [2,1], [3,1], [2,1], [6,1],
|
||||||
|
[2,1], [3,1], [2,1], [5,1], [4,2,1], [3,1], [2,1], [1],
|
||||||
|
[2,1], [3,1], [2,1], [4,1], [2,1], [3,1], [2,1], [1],
|
||||||
|
[2,1], [3,1], [2,1], [5,1], [4,2,1], [3,1], [2,1], [1],
|
||||||
|
[2,1], [3,1], [2,1], [4,1], [2,1], [3,1], [2,1], [7,1],
|
||||||
|
[2,1], [3,1], [6,2,1],[5,1], [4,2,1], [3,1], [2,1], [1],
|
||||||
|
];
|
||||||
|
|
||||||
|
function _calculateCardLabelPosition(boxN){
|
||||||
|
|
||||||
|
var x = 0;
|
||||||
|
var y = 0;
|
||||||
|
|
||||||
|
x += boxN*62.5;
|
||||||
|
y += canvas.height/2 - 60;
|
||||||
|
|
||||||
|
y += -ANIM_BOXES[boxN];
|
||||||
|
y += -ANIM_CARDS[boxN]/3;
|
||||||
|
|
||||||
|
x += 60/2;
|
||||||
|
y -= 13;
|
||||||
|
|
||||||
|
if(y<50) y=50;
|
||||||
|
|
||||||
|
return {
|
||||||
|
x:x,
|
||||||
|
y:y
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var annPass = null;
|
||||||
|
var annFail = null;
|
||||||
|
var annAdd = null;
|
||||||
|
function _clearAnnotations(){
|
||||||
|
annPass = null;
|
||||||
|
annFail = null;
|
||||||
|
annAdd = null;
|
||||||
|
};
|
||||||
|
function _annotatePass(from, N){
|
||||||
|
if(N>0) annPass = {from:from, N:N};
|
||||||
|
}
|
||||||
|
function _annotateFail(from, N){
|
||||||
|
if(N>0) annFail = {from:from, N:N};
|
||||||
|
}
|
||||||
|
function _annotateAdd(N){
|
||||||
|
annAdd = {N:N};
|
||||||
|
}
|
||||||
|
|
||||||
|
window.REDRAW = 0;
|
||||||
|
function update(){
|
||||||
|
|
||||||
|
// Don't re-draw unnecessarily!
|
||||||
|
if(window.REDRAW>0){
|
||||||
|
|
||||||
|
window.REDRAW -= 1;
|
||||||
|
|
||||||
|
// Clear & Retina
|
||||||
|
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
|
||||||
|
ctx.save();
|
||||||
|
ctx.scale(2,2);
|
||||||
|
|
||||||
|
// Draw boxes, 1 to 7
|
||||||
|
for(var i=0;i<BOXES.length;i++){
|
||||||
|
|
||||||
|
var w = 60;
|
||||||
|
var h = 60;
|
||||||
|
|
||||||
|
// ANIMATED!
|
||||||
|
ANIM_CARDS[i] = ANIM_CARDS[i]*ANIM_EASE + BOXES[i]*(1-ANIM_EASE);
|
||||||
|
// Card labels...
|
||||||
|
ctx.fillStyle = "#bbb";
|
||||||
|
ctx.font = "20px PatrickHand";
|
||||||
|
ctx.textAlign = "center";
|
||||||
|
ctx.textBaseline = "middle";
|
||||||
|
var pos = _calculateCardLabelPosition(i);
|
||||||
|
ctx.fillText(Math.round(ANIM_CARDS[i]), pos.x, pos.y);
|
||||||
|
|
||||||
|
|
||||||
|
// Transform...
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(i*62.5, canvas.height/2-h);
|
||||||
|
|
||||||
|
// Bounce! ANIMATED!
|
||||||
|
var elevation = (CURRENTLY_REVIEWED==i) ? 10 : 0;
|
||||||
|
ANIM_BOXES[i] = ANIM_BOXES[i]*ANIM_EASE + elevation*(1-ANIM_EASE);
|
||||||
|
ctx.translate(0, -ANIM_BOXES[i]);
|
||||||
|
|
||||||
|
// The cards inside me
|
||||||
|
var cardsHeight = ANIM_CARDS[i]/3;
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(0, -cardsHeight);
|
||||||
|
ctx.fillStyle = "rgba(0,0,0,0.2)";
|
||||||
|
ctx.fillRect(5, 0, w-10, cardsHeight);
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Am I active today?
|
||||||
|
var activeToday = (QUEUE.indexOf(i+1)>=0); // +1 coz offset
|
||||||
|
if(CURRENTLY_REVIEWED==i) activeToday=true; // also if CURRENTLY reviewed...
|
||||||
|
var color = COLORS[i];
|
||||||
|
|
||||||
|
// Fill
|
||||||
|
ctx.fillStyle = activeToday ? color : "#fff";
|
||||||
|
ctx.fillRect(0, 0, 60, 60);
|
||||||
|
|
||||||
|
// Stroke
|
||||||
|
if(!activeToday){
|
||||||
|
ctx.strokeStyle = color;
|
||||||
|
var lw = 2;
|
||||||
|
ctx.lineWidth = lw;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.rect(lw/2, lw/2, w-lw, h-lw);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Number
|
||||||
|
ctx.font = "50px PatrickHand";
|
||||||
|
ctx.textAlign = "center";
|
||||||
|
ctx.textBaseline = "middle";
|
||||||
|
ctx.fillStyle = activeToday ? "#fff": color;
|
||||||
|
ctx.fillText((i+1), w/2, h/2);
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw Annotations!
|
||||||
|
if(annFail){
|
||||||
|
_drawArrow(true);
|
||||||
|
}
|
||||||
|
if(annPass){
|
||||||
|
_drawArrow();
|
||||||
|
}
|
||||||
|
if(annAdd){
|
||||||
|
|
||||||
|
// The number...
|
||||||
|
ctx.fillStyle = "#000";
|
||||||
|
ctx.font = "18px PatrickHand";
|
||||||
|
ctx.textAlign = "center";
|
||||||
|
ctx.textBaseline = "bottom";
|
||||||
|
var label = "+" + annAdd.N;
|
||||||
|
var pos = _calculateCardLabelPosition(0);
|
||||||
|
ctx.fillText(label, pos.x, pos.y-15);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// And, again...
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(update);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function _drawArrow(fail){
|
||||||
|
|
||||||
|
var ann = fail ? annFail : annPass;
|
||||||
|
|
||||||
|
// Points
|
||||||
|
var pos1 = _calculateCardLabelPosition(ann.from);
|
||||||
|
var pos2 = _calculateCardLabelPosition(fail ? 0 : ann.from+1);
|
||||||
|
var xOffset = fail ? -5 : 5;
|
||||||
|
pos1.x += xOffset;
|
||||||
|
pos1.y -= 15;
|
||||||
|
pos2.x -= xOffset;
|
||||||
|
pos2.y -= 15;
|
||||||
|
var cp = {
|
||||||
|
x: (pos1.x+pos2.x)/2,
|
||||||
|
y: Math.min(pos1.y,pos2.y)-30
|
||||||
|
};
|
||||||
|
|
||||||
|
// Color!
|
||||||
|
var color = fail ? COLORS[0] : COLORS[3]; // red/green
|
||||||
|
|
||||||
|
// Arrow
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(pos1.x, pos1.y);
|
||||||
|
ctx.quadraticCurveTo(cp.x, cp.y, pos2.x, pos2.y);
|
||||||
|
ctx.strokeStyle = color;
|
||||||
|
var w = ann.N/10;
|
||||||
|
if(w<1) w=1;
|
||||||
|
if(w>7) w=7;
|
||||||
|
ctx.lineWidth = w;
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// ArrowHEAD
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(pos2.x, pos2.y);
|
||||||
|
var dy = pos2.y - cp.y;
|
||||||
|
var dx = pos2.x - cp.x;
|
||||||
|
var rotation = Math.atan2(dy,dx);
|
||||||
|
ctx.rotate(rotation-Math.PI/2);
|
||||||
|
ctx.beginPath();
|
||||||
|
var arrSize = w*1.5;
|
||||||
|
if(arrSize<5) arrSize=5;
|
||||||
|
ctx.moveTo(-arrSize,-arrSize);
|
||||||
|
ctx.lineTo(0,0);
|
||||||
|
ctx.lineTo(arrSize,-arrSize);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
|
// The number...
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.font = "18px PatrickHand";
|
||||||
|
ctx.textAlign = "center";
|
||||||
|
ctx.textBaseline = "bottom";
|
||||||
|
var label = (fail ? "-" : "+") + ann.N;
|
||||||
|
ctx.fillText(label, cp.x, cp.y+15);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>The Flashcard</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="../fcard.css"/>
|
||||||
|
<link rel="stylesheet" type="text/css" href="multicard.css"/>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="container">
|
||||||
|
|
||||||
|
<!-- Cards! -->
|
||||||
|
<div id="cards">
|
||||||
|
|
||||||
|
<!-- BG -->
|
||||||
|
<div id="card_bg" class="card">
|
||||||
|
<div id="card_bg_smiley">☻</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Next card is behind -->
|
||||||
|
<div id="next_card" class="card"></div>
|
||||||
|
|
||||||
|
<!-- Current card is on top -->
|
||||||
|
<div id="current_card">
|
||||||
|
<div class="scale_on_hover">
|
||||||
|
<div id="flip-container">
|
||||||
|
<div class="flipper">
|
||||||
|
<div id="ccard_front" class="card"></div>
|
||||||
|
<div id="ccard_back" class="card"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Did you get it right? -->
|
||||||
|
<div id="info">
|
||||||
|
<div id="question"></div>
|
||||||
|
<div id="answer" style="display:none">
|
||||||
|
<div id="a_label"></div>
|
||||||
|
<div id="a_no"></div>
|
||||||
|
<div id="a_yes"></div>
|
||||||
|
</div>
|
||||||
|
<div id="done" style="display:none"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Default Labels -->
|
||||||
|
<div id="default_labels">
|
||||||
|
|
||||||
|
<span id="multicard_q">
|
||||||
|
try to recall ↑
|
||||||
|
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>
|
||||||
|
↓
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- Flashcards -->
|
||||||
|
<span id="flashcard_test_a_front">
|
||||||
|
A?
|
||||||
|
</span>
|
||||||
|
<span id="flashcard_test_a_back">
|
||||||
|
Apple
|
||||||
|
</span>
|
||||||
|
<span id="flashcard_test_b_front">
|
||||||
|
B?
|
||||||
|
</span>
|
||||||
|
<span id="flashcard_test_b_back">
|
||||||
|
Banana
|
||||||
|
</span>
|
||||||
|
<span id="flashcard_test_c_front">
|
||||||
|
C?
|
||||||
|
</span>
|
||||||
|
<span id="flashcard_test_c_back">
|
||||||
|
COOL BEANS
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
||||||
|
<script src="../helpers.js"></script>
|
||||||
|
<script src="multicard.js"></script>
|
|
@ -0,0 +1,160 @@
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Container */
|
||||||
|
#container{
|
||||||
|
/*background: #eee;*/
|
||||||
|
width: 600px;
|
||||||
|
height: 400px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cards */
|
||||||
|
#cards{
|
||||||
|
position: absolute;
|
||||||
|
left: 100px;
|
||||||
|
top: 35px;
|
||||||
|
/* so tl centers the 400x240 cards in 600x400 container */
|
||||||
|
}
|
||||||
|
.card{
|
||||||
|
position: absolute;
|
||||||
|
width: 390px; /* 5 x 3 */
|
||||||
|
height: 230px;
|
||||||
|
border-radius: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 40px;
|
||||||
|
background: #fff;
|
||||||
|
border: 5px inset rgba(0,0,0,0.15);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
#card_bg{
|
||||||
|
background: #888;
|
||||||
|
border-color: rgba(0,0,0,0);
|
||||||
|
}
|
||||||
|
#card_bg_smiley{
|
||||||
|
color: rgba(255,255,255,0.5);
|
||||||
|
font-size: 100px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 230px;
|
||||||
|
font-size: 200px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#next_card{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#current_card{
|
||||||
|
position: absolute;
|
||||||
|
top:0;
|
||||||
|
left:0;
|
||||||
|
width: 400px;
|
||||||
|
height: 240px;
|
||||||
|
|
||||||
|
transition: left 0.3s ease-in-out;
|
||||||
|
position: relative;
|
||||||
|
left:0;
|
||||||
|
}
|
||||||
|
.scale_on_hover{
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
transition: 0.15s;
|
||||||
|
transform-origin: 200px 120px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.scale_on_hover:hover{
|
||||||
|
transform: scale(1.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************/
|
||||||
|
/***** CURRENT CARD ******/
|
||||||
|
/*************************/
|
||||||
|
|
||||||
|
#flip-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
perspective: 1200px;
|
||||||
|
}
|
||||||
|
#flip-container .flipper {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
transition: 0.5s;
|
||||||
|
transform-style: preserve-3d;
|
||||||
|
transform-origin: 200px 120px;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
#flip-container[flip=yes] .flipper{
|
||||||
|
transform: rotateY(180deg);
|
||||||
|
}
|
||||||
|
#ccard_front, #ccard_back {
|
||||||
|
backface-visibility: hidden;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
#ccard_front {
|
||||||
|
z-index: 2;
|
||||||
|
transform: rotateY(0deg);
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
#ccard_back {
|
||||||
|
transform: rotateY(180deg);
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Q & A */
|
||||||
|
#info{
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
top: 290px;
|
||||||
|
font-size: 25px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.2em;
|
||||||
|
}
|
||||||
|
#question{
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
#question span{
|
||||||
|
color: #777;
|
||||||
|
}
|
||||||
|
#a_label{
|
||||||
|
margin-bottom: 0.25em;
|
||||||
|
}
|
||||||
|
#a_no, #a_yes{
|
||||||
|
display: inline-block;
|
||||||
|
width: 140px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 10px;
|
||||||
|
color: rgba(0,0,0,0.7);
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 40px;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: 0.1s;
|
||||||
|
position: relative;
|
||||||
|
top:0;
|
||||||
|
}
|
||||||
|
#a_no:hover, #a_yes:hover{
|
||||||
|
top:-2px;
|
||||||
|
}
|
||||||
|
#a_no{
|
||||||
|
background: #ff4040;
|
||||||
|
}
|
||||||
|
#a_yes{
|
||||||
|
background: #40ff40;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LABELS */
|
||||||
|
#default_labels{
|
||||||
|
display:none;
|
||||||
|
}
|
|
@ -0,0 +1,222 @@
|
||||||
|
window.CARDS = [];
|
||||||
|
window.onload = function(){
|
||||||
|
|
||||||
|
// Get cards, in order.
|
||||||
|
var cardnames = _getQueryVariable("cards");
|
||||||
|
cardnames = cardnames.split(",");
|
||||||
|
window.CARDS = cardnames.map(function(cardname){
|
||||||
|
return {
|
||||||
|
front: _getLabel("flashcard_"+cardname+"_front"),
|
||||||
|
back: _getLabel("flashcard_"+cardname+"_back")
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set up info
|
||||||
|
setUpInfo();
|
||||||
|
|
||||||
|
// Logic for flippable current card
|
||||||
|
$("#current_card").onclick = function(){
|
||||||
|
|
||||||
|
// Flip!
|
||||||
|
var flipcont = $("#flip-container");
|
||||||
|
var flip = flipcont.getAttribute("flip");
|
||||||
|
flipcont.setAttribute("flip", (flip=="yes") ? "no" : "yes");
|
||||||
|
|
||||||
|
// Hide Info, then ask "did you get it?"
|
||||||
|
if(INFO_MODE == "question"){
|
||||||
|
hideInfo();
|
||||||
|
setTimeout(showInfoAnswer, 700);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// Show first card
|
||||||
|
showCurrentCard();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
function showCurrentCard(infoTimeout){
|
||||||
|
|
||||||
|
// NO MORE CARDS? guess we're DONE.
|
||||||
|
|
||||||
|
if(CARDS.length==0){
|
||||||
|
|
||||||
|
$("#next_card").style.display = "none";
|
||||||
|
$("#current_card").style.display = "none";
|
||||||
|
showInfoDone();
|
||||||
|
|
||||||
|
}else{
|
||||||
|
|
||||||
|
// Card
|
||||||
|
var currentCard = CARDS[0];
|
||||||
|
$("#ccard_front").innerHTML = currentCard.front;
|
||||||
|
$("#ccard_back").innerHTML = currentCard.back;
|
||||||
|
|
||||||
|
_modifyFlashCard($("#ccard_front"));
|
||||||
|
_modifyFlashCard($("#ccard_back"));
|
||||||
|
|
||||||
|
// INSTANT RESET
|
||||||
|
|
||||||
|
var flipper = $("#flip-container .flipper");
|
||||||
|
flipper.style.transition = "0s"; // INSTANT
|
||||||
|
$("#flip-container").setAttribute("flip","no");
|
||||||
|
|
||||||
|
var ccardDOM = $("#current_card");
|
||||||
|
ccardDOM.style.transition = "0s"; // INSTANT
|
||||||
|
ccardDOM.style.left = "";
|
||||||
|
|
||||||
|
setTimeout(function(){
|
||||||
|
flipper.style.transition = "";
|
||||||
|
ccardDOM.style.transition = "";
|
||||||
|
},PLANCK_TIME);
|
||||||
|
|
||||||
|
|
||||||
|
// Question
|
||||||
|
infoTimeout = infoTimeout || PLANCK_TIME;
|
||||||
|
setTimeout(function(){
|
||||||
|
showInfoQuestion();
|
||||||
|
},infoTimeout);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function showNextCard(){
|
||||||
|
if(CARDS.length>0){
|
||||||
|
var nextCard = CARDS[0];
|
||||||
|
$("#next_card").style.display = "block";
|
||||||
|
$("#next_card").innerHTML = nextCard.front;
|
||||||
|
}else{
|
||||||
|
$("#card_bg_smiley").style.display = "block";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var PLANCK_TIME = 20;
|
||||||
|
var BUFFER_TIME = PLANCK_TIME*3;
|
||||||
|
|
||||||
|
function nextCard(removeCurrent){
|
||||||
|
|
||||||
|
hideInfo();
|
||||||
|
|
||||||
|
// Remove, or shuffle back?
|
||||||
|
var currCard = CARDS.shift();
|
||||||
|
if(!removeCurrent){
|
||||||
|
CARDS.push(currCard); // shuffle to back.
|
||||||
|
}
|
||||||
|
|
||||||
|
// REMOVE... CARD TO THE RIGHT!
|
||||||
|
if(removeCurrent){
|
||||||
|
|
||||||
|
showNextCard();
|
||||||
|
|
||||||
|
var currentCard = $("#current_card");
|
||||||
|
currentCard.style.left = "500px";
|
||||||
|
|
||||||
|
setTimeout(function(){
|
||||||
|
$("#next_card").style.display = "none";
|
||||||
|
showCurrentCard();
|
||||||
|
},300+BUFFER_TIME);
|
||||||
|
|
||||||
|
}else{
|
||||||
|
|
||||||
|
showNextCard();
|
||||||
|
|
||||||
|
var currentCard = $("#current_card");
|
||||||
|
currentCard.style.left = "-400px";
|
||||||
|
|
||||||
|
setTimeout(function(){
|
||||||
|
|
||||||
|
// What card is currently showing?
|
||||||
|
var cardShowing = ($("#flip-container").getAttribute("flip")=="yes") ? "#ccard_back" : "#ccard_front";
|
||||||
|
var htmlShowing = $(cardShowing).innerHTML;
|
||||||
|
|
||||||
|
// Force next card to look like past card, and teleport it out
|
||||||
|
var ncard = $("#next_card");
|
||||||
|
ncard.innerHTML = htmlShowing;
|
||||||
|
ncard.style.left = "-400px";
|
||||||
|
|
||||||
|
// Slide next card to where current card should be
|
||||||
|
setTimeout(function(){
|
||||||
|
ncard.style.transition = "left 0.3s ease-out";
|
||||||
|
setTimeout(function(){
|
||||||
|
ncard.style.left = "0px";
|
||||||
|
setTimeout(function(){
|
||||||
|
ncard.style.transition = "";
|
||||||
|
ncard.style.left = "";
|
||||||
|
ncard.style.display = "none"; // BYE.
|
||||||
|
},300+BUFFER_TIME);
|
||||||
|
},PLANCK_TIME);
|
||||||
|
},PLANCK_TIME);
|
||||||
|
|
||||||
|
// Also, show current card
|
||||||
|
showCurrentCard(300+BUFFER_TIME);
|
||||||
|
|
||||||
|
},300+BUFFER_TIME);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function setUpInfo(){
|
||||||
|
|
||||||
|
// Labels
|
||||||
|
$("#a_label").innerHTML = _getLabel("multicard_a");
|
||||||
|
$("#a_no").innerHTML = _getLabel("multicard_no");
|
||||||
|
$("#a_yes").innerHTML = _getLabel("multicard_yes");
|
||||||
|
$("#done").innerHTML = _getLabel("multicard_done");
|
||||||
|
|
||||||
|
// Clicking "yes" or "no"
|
||||||
|
$("#a_yes").onclick = function(){
|
||||||
|
nextCard(true);
|
||||||
|
};
|
||||||
|
$("#a_no").onclick = function(){
|
||||||
|
nextCard(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var INFO_MODE = "question";
|
||||||
|
function showInfoQuestion(){
|
||||||
|
|
||||||
|
$("#info").style.display = "block";
|
||||||
|
$("#question").style.display = "block";
|
||||||
|
$("#answer").style.display = "none";
|
||||||
|
|
||||||
|
// Show, with "(how many left)"
|
||||||
|
var html = _getLabel("multicard_q");
|
||||||
|
html += "<br>";
|
||||||
|
html += "<span>";
|
||||||
|
html += _getLabel("multicard_cards_left").replace("[N]",CARDS.length);
|
||||||
|
html += "</span>";
|
||||||
|
$("#question").innerHTML = html;
|
||||||
|
|
||||||
|
INFO_MODE = "question";
|
||||||
|
|
||||||
|
}
|
||||||
|
function showInfoAnswer(){
|
||||||
|
$("#info").style.display = "block";
|
||||||
|
$("#question").style.display = "none";
|
||||||
|
$("#answer").style.display = "block";
|
||||||
|
INFO_MODE = "answer";
|
||||||
|
}
|
||||||
|
function showInfoDone(){
|
||||||
|
$("#info").style.display = "block";
|
||||||
|
$("#question").style.display = "none";
|
||||||
|
$("#answer").style.display = "none";
|
||||||
|
$("#done").style.display = "block";
|
||||||
|
INFO_MODE = "done";
|
||||||
|
}
|
||||||
|
function hideInfo(){
|
||||||
|
$("#info").style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Also, Send message when ALL DONE
|
||||||
|
if(!FLIPPED && window.top.broadcastMessage){
|
||||||
|
FLIPPED = true;
|
||||||
|
setTimeout(function(){
|
||||||
|
window.top.broadcastMessage("flip_"+cardname);
|
||||||
|
},1000);
|
||||||
|
}
|
||||||
|
*/
|
|
@ -0,0 +1,26 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>The Flashcard</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="../fcard.css"/>
|
||||||
|
<link rel="stylesheet" type="text/css" href="singlecard.css"/>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="container">
|
||||||
|
<div id="flashcard" class="flip-container">
|
||||||
|
<div class="flipper">
|
||||||
|
<div id="front" class="front"></div>
|
||||||
|
<div id="back" class="back"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
||||||
|
<script src="../helpers.js"></script>
|
||||||
|
<script src="singlecard.js"></script>
|
|
@ -0,0 +1,70 @@
|
||||||
|
/* 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;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
font-size: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* entire container, keeps perspective */
|
||||||
|
#container{
|
||||||
|
width: 440px;
|
||||||
|
height: 300px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.flip-container {
|
||||||
|
transition: 0.15s;
|
||||||
|
transform-origin: 200px 120px;
|
||||||
|
perspective: 1200px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.flip-container:hover {
|
||||||
|
transform: scale(1.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Flip it! */
|
||||||
|
.flip-container[flip=yes] .flipper{
|
||||||
|
transform: rotateY(180deg);
|
||||||
|
}
|
||||||
|
.front, .back {
|
||||||
|
width: 400px; /* 5 x 3 */
|
||||||
|
height: 240px;
|
||||||
|
border-radius: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.flipper {
|
||||||
|
transition: 0.5s;
|
||||||
|
transform-style: preserve-3d;
|
||||||
|
transform-origin: 200px 120px;
|
||||||
|
position: absolute;
|
||||||
|
left: 20px;
|
||||||
|
top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Front & Back */
|
||||||
|
.front, .back {
|
||||||
|
backface-visibility: hidden;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.front {
|
||||||
|
z-index: 2;
|
||||||
|
transform: rotateY(0deg);
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.back {
|
||||||
|
transform: rotateY(180deg);
|
||||||
|
background: #fff;
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
var flashcard = $("#flashcard");
|
||||||
|
var FLIPPED = false;
|
||||||
|
flashcard.onclick = function(){
|
||||||
|
|
||||||
|
// Flip!
|
||||||
|
var flip = flashcard.getAttribute("flip");
|
||||||
|
if(flip=="yes"){
|
||||||
|
flashcard.setAttribute("flip","no");
|
||||||
|
}else{
|
||||||
|
flashcard.setAttribute("flip","yes");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also, send message (when flipped for first time)
|
||||||
|
if(!FLIPPED && window.top.broadcastMessage){
|
||||||
|
FLIPPED = true;
|
||||||
|
setTimeout(function(){
|
||||||
|
window.top.broadcastMessage("flip_"+cardname);
|
||||||
|
},1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
window.cardname = _getQueryVariable("card");
|
||||||
|
var frontHTML = _getLabel("flashcard_"+cardname+"_front");
|
||||||
|
var backHTML = _getLabel("flashcard_"+cardname+"_back");
|
||||||
|
$("#front").innerHTML = frontHTML;
|
||||||
|
$("#back").innerHTML = backHTML;
|
||||||
|
|
||||||
|
_modifyFlashCard($("#front"));
|
||||||
|
_modifyFlashCard($("#back"));
|
|
@ -0,0 +1,218 @@
|
||||||
|
#THE MEMORY THING
|
||||||
|
|
||||||
|
## Why & Science
|
||||||
|
|
||||||
|
Pffft, why bother *memorizing* anything? Nowadays, when you can use a giant techno-corp to look stuff up, what is memory good for other than party tricks like: remembering the names & lives of people you've met, knowing your favorite poems & passages by heart, and having a rich internal library you can take with you anywhere?
|
||||||
|
|
||||||
|
But seriously: memory gets a bad rap. I remember being forced to memorize countless meaningless facts in school.
|
||||||
|
|
||||||
|
But the problem isn't *memorization*, it's *meaninglessness*. If done well, memory *makes* meaning! There's a reason why, in Greek mythology, Memory was the mother of the Muses.
|
||||||
|
|
||||||
|
The Greeks were right. You may have heard that we should teach creativity & critical thinking *instead* of rote memorization – but cognitive scientists have shown that creativity & critical thinking *require* memorization! (Proof: imagine composing a poem or essay if you haven't even memorized your ABCs) Before we can *connect* the dots, we must first *collect* the dots.
|
||||||
|
|
||||||
|
But people use many strategies to learn, and most of them suck. In 2013, a team of cognitive scientists did a meta-study – a study of _200+ other studies_ – to find out how effective 10 study techniques were. Let's see if their findings match your expectations. (they didn't match *mine!*)
|
||||||
|
|
||||||
|
**Re-reading** to remember is...
|
||||||
|
|
||||||
|
[NOT EFFECTIVE]
|
||||||
|
|
||||||
|
A lot of students re-read text to study. It's hard, it's time-consuming, but at least it's... mostly useless. Oh well. On to the next:
|
||||||
|
|
||||||
|
**Highlighting** or underlining key material is...
|
||||||
|
|
||||||
|
[NOT EFFECTIVE]
|
||||||
|
|
||||||
|
That's right – despite re-reading & highlighting being the most common study techniques, they've both been proven to *NOT* work. Now, how about this:
|
||||||
|
|
||||||
|
**Spaced Practice**, spreading out your learning instead of cramming, is...
|
||||||
|
|
||||||
|
[HIGHLY EFFECTIVE!]
|
||||||
|
|
||||||
|
We're all guilty of cramming the night before the exam. And a month after the exam, you've forgotten everything. In contrast, spacing out your learning is easier _and_ helps you recall things better in the long term! Next:
|
||||||
|
|
||||||
|
**Visual Mnemonics**, making mental pictures for ideas you want to memorize, is...
|
||||||
|
|
||||||
|
[NOT EFFECTIVE]
|
||||||
|
|
||||||
|
This shocked me. Especially since I *love* visualization. But although imagery helps with learning foreign or technical vocabulary, it's not been shown to work over the long term like spaced practice does. Finally:
|
||||||
|
|
||||||
|
**Active Recall**, giving yourself quizzes, is...
|
||||||
|
|
||||||
|
[HIGHLY EFFECTIVE!]
|
||||||
|
|
||||||
|
You learn what you practice. The reason re-reading & highlighting is so ineffective is that you don't practice *recalling* answers, you practice *recognizing* answers.
|
||||||
|
|
||||||
|
There's a method that combines spaced practice and active recall. It's called **spaced repetition.** (I think "spaced recall" would be a better name, but alas) Compared to the most common study techniques, spaced repetition is easier, more effective, and *almost no schools tell you about it*.
|
||||||
|
|
||||||
|
// Note: Memory Palaces?
|
||||||
|
|
||||||
|
But I want to tell you about it. In the next 10 minutes, I'd like to help you start using spaced repetition *today*. Whether you're in school (my condolences) or learning on your own (my congratulations), I hope this little method can be the mother of your Muses.
|
||||||
|
|
||||||
|
But first... let's do a little active recall, shall we?
|
||||||
|
|
||||||
|
**TEST A:**
|
||||||
|
|
||||||
|
[3 Common But Ineffective Ways to Learn are...]
|
||||||
|
[Re-reading, Highlighting, Visual Mnemonics]
|
||||||
|
|
||||||
|
[2 Uncommon But Effective Ways to Learn are...]
|
||||||
|
[Spaced Practice, Active Recall]
|
||||||
|
|
||||||
|
[???]
|
||||||
|
[???]
|
||||||
|
|
||||||
|
## How & What
|
||||||
|
|
||||||
|
So, what, am I just recommending you use boring ol' flashcards? Well, yes. But they're flashcards... *on steroids!*
|
||||||
|
|
||||||
|
The problem with regular flashcards is that *you* decide when you see the cards. You may see cards you know as often as cards you don't. Or you may throw away cards you *think* you have in long-term memory, but really was only in short-term memory. The solution: have a shoebox tell you when to study.
|
||||||
|
|
||||||
|
**The Leitner Box** was invented by Sebastian Leitner in 1973, and was one of the first spaced repetition systems. It's a box. An actual box. (There's a digital version; we'll see that later) You can use this box to learn almost anything, but it's most popular in the language-learning community. Earlier this year, I started using this box to help me learn French – and in two *months*, the box taught me more than two *years* of Canadian school French.
|
||||||
|
|
||||||
|
Not bad for a shoebox! Here's how it works.
|
||||||
|
|
||||||
|
**To make the box:**
|
||||||
|
|
||||||
|
1. Get a box.
|
||||||
|
2. Make dividers to split the box into levels. (let's say 7 levels)
|
||||||
|
|
||||||
|
**To use the box:**
|
||||||
|
|
||||||
|
You put flashcards in the box.
|
||||||
|
|
||||||
|
// how flashcards work
|
||||||
|
|
||||||
|
Here's how often you should study (active recall) the cards at each level:
|
||||||
|
|
||||||
|
[pic]
|
||||||
|
|
||||||
|
Note that for each next level, the duration *doubles*. (spaced practice) Here's a 64-day calendar you can use:
|
||||||
|
|
||||||
|
[pic]
|
||||||
|
|
||||||
|
// (Note that you should review your higher-level cards _first_.)
|
||||||
|
|
||||||
|
But how do your cards get to each level? Well, first, new flashcards go into Level 1.
|
||||||
|
|
||||||
|
[pic]
|
||||||
|
|
||||||
|
When you review the cards in a single level, all the cards you got right go up to the next level, and all the cards you got wrong go... _back to Level 1._
|
||||||
|
|
||||||
|
[pic]
|
||||||
|
|
||||||
|
// (And if you're reviewing Level 1, just keep reviewing your incorrect cards until they all go up to Level 2)
|
||||||
|
|
||||||
|
This guarantees that you'll see cards you don't know often, and cards you do know only _just as you're about to forget them_. It's a system that strengthens your weakest points!
|
||||||
|
|
||||||
|
Here's a step-by-step simulation of what this looks like over several days:
|
||||||
|
|
||||||
|
[sim]
|
||||||
|
|
||||||
|
Now, for whatever reason, you may not like the idea of scribbling index cards to shove into a shoebox. Thankfully, there are also digital spaced repetition systems, the most popular of which is the free, open-source app, **Anki.**
|
||||||
|
|
||||||
|
(Anki uses a more sophisticated spaced-practice schedule than the Leitner box, but the core idea is the same.)
|
||||||
|
|
||||||
|
(ANTI-DISCLAIMER: I am *not* affiliated with Anki, I just genuinely think it's an awesome app. Even though I personally prefer the shoebox.)
|
||||||
|
|
||||||
|
Alright, now that I've sung the high praises of a shoebox, here's the catch:
|
||||||
|
|
||||||
|
// Only for declarative knowledge (but procedural skills can be aided)
|
||||||
|
|
||||||
|
// MEMORIZATION IS NOT ENOUGH (collect THEN connect. on the side, do stuff that uses the facts together)
|
||||||
|
|
||||||
|
**No matter what fancy learning techniques you use, if what you're learning feels meaningless, it won't work.** As I said earlier: the problem isn't memorization, it's meaninglessness. It's not knowing your *whats*, it's knowing your *why*.
|
||||||
|
|
||||||
|
So now, let's turn to you. What do you want to learn, and why?
|
||||||
|
|
||||||
|
∴ TEST B (3 cards)
|
||||||
|
|
||||||
|
[What does the "Forgetting Curve" look like?] []
|
||||||
|
|
||||||
|
[Leitner Box study schedule] []
|
||||||
|
|
||||||
|
[The most popular Spaced Repetition System app (as of Sep 2018) is...] [Anki (free, open source)]
|
||||||
|
|
||||||
|
## Using it for YOU
|
||||||
|
|
||||||
|
"Meaning" may seem like a fluffy woo-word, but it all comes down to the lament of bored students everywhere:
|
||||||
|
|
||||||
|
“When am I ever going to use this?”
|
||||||
|
|
||||||
|
Before I started using the shoebox, I had tried learning French with Duolingo. It's a great app. I'm not dissing it. It's free, it's polished, and it's helped lots of people. But not me. Learning a list of words (that I didn't choose) felt too removed from what I *really* wanted to do with the language, which was read French comics and be a French film snob.
|
||||||
|
|
||||||
|
But when I tried learning French again with the shoebox, I was forced to make my *own* list of words. And I got new vocabulary from the thing I *actually* cared about: Tintin comics! And the more words I learnt from Tintin, the better I got at reading Tintin. **The thing I actually cared about was both my source & destination.**
|
||||||
|
|
||||||
|
// pic
|
||||||
|
|
||||||
|
I think *that's* the trick. Not just for memorizing French, not even just for memorizing, but for *all learning*. It needs to go into (and ideally, also come from) things you love.
|
||||||
|
|
||||||
|
// pics: (examples involving memory)
|
||||||
|
// programming
|
||||||
|
// ukulele
|
||||||
|
// kinds of birds (thing itself)
|
||||||
|
|
||||||
|
That's why the consensus among spaced repetition enthusiasts is that, in almost all cases, **you should make your own cards.** That way, you have a chance to make your cards meaningful to *you*.
|
||||||
|
|
||||||
|
Speaking of which, it's time to make your own cards:
|
||||||
|
|
||||||
|
**WHAT do you want to learn?**
|
||||||
|
|
||||||
|
If you want to start small, here's some stuff you could memorize in a few days: Morse code, Braille, the ASL alphabet, US states & their capitals, the countries in your continent, etc
|
||||||
|
|
||||||
|
**WHY do you want to learn X?**
|
||||||
|
|
||||||
|
What do you want to use your knowledge *for?* (If it's for the sake of the knowledge itself, that's great! Write that.)
|
||||||
|
|
||||||
|
Examples: "Programming -> to make games"
|
||||||
|
|
||||||
|
**WHERE will you get your cards from?**
|
||||||
|
|
||||||
|
sadasdsaasdasas
|
||||||
|
|
||||||
|
But after all this, will spaced repetition be worth it for you? The results of spaced repetition only *really* show themselves after weeks or months. That's why spaced repetition isn't as popular compared to highlighting or re-reading: although it's more effective and just as easy, it *takes longer.* It's too bad we can't get a sneak peek into the future.
|
||||||
|
|
||||||
|
Except we can. Here's a simulation-based calculator to show you how much *you* can learn with a few minutes a day over a month or so...
|
||||||
|
|
||||||
|
// sim
|
||||||
|
|
||||||
|
And finally, just one final self-test:
|
||||||
|
|
||||||
|
∴ TEST A, B, C
|
||||||
|
|
||||||
|
## Get Started Right Now
|
||||||
|
|
||||||
|
Leitner Box:
|
||||||
|
|
||||||
|
* + Arts & Craftsy!
|
||||||
|
* + You can easily modify rules
|
||||||
|
* - Can't do sound
|
||||||
|
* - Not easily portable
|
||||||
|
|
||||||
|
Anki:
|
||||||
|
|
||||||
|
* + Most popular
|
||||||
|
* + Can do sound
|
||||||
|
* + Portable: laptop and mobile web versions
|
||||||
|
* + Can customize decks and rules
|
||||||
|
* - Clunky interface
|
||||||
|
|
||||||
|
Tinycards:
|
||||||
|
|
||||||
|
* + Very well-designed and polished
|
||||||
|
* + Easy to try out: no need to download or even sign up
|
||||||
|
* - Not open source
|
||||||
|
* - Less customizeable than Anki
|
||||||
|
|
||||||
|
GET STARTED
|
||||||
|
|
||||||
|
**Leitner Box:** go to a nearby office supply store and buy flashcards, and a box for the flashcards. (you can also buy dividers, or make your own) print out your study calendar
|
||||||
|
|
||||||
|
**Anki:** Download it here. Here's a tutorial by Fluent Forever
|
||||||
|
|
||||||
|
**Tinycards.** No need for buying supplies or download or even signing up at first. Go to X and try out a few flashcards!
|
||||||
|
|
||||||
|
In 1953, Henry Molaison had a brain surgery to get rid of his epilepsy. It also got rid of his ability to make new memories. From then on, he lived in a perpetual present. He died at 82. He stopped at 27.
|
||||||
|
|
||||||
|
Memory is not – should not – be late nights re-reading a textbook that feels heavier than your 2AM eyelids. Memory makes us human. Memory is the mother of all our muses. It's a shame that school has left too many of us with a Pavlovian fear-response of "learning" and "memorization". I hope this interactive comic helped ease some of those fears, and given you a simple, concrete way to take back your mind, and learn better. And you'll finally recognize memory for what it is:
|
||||||
|
|
||||||
|
It's a *superpower.*
|
|
@ -0,0 +1,218 @@
|
||||||
|
#THE MEMORY THING
|
||||||
|
|
||||||
|
## Why & Science
|
||||||
|
|
||||||
|
Pffft, why bother *memorizing* anything? Nowadays, when you can use a giant techno-corp to look stuff up, what is memory good for other than party tricks like: remembering the names & lives of people you've met, knowing your favorite poems & passages by heart, and having a rich internal library you can take with you anywhere?
|
||||||
|
|
||||||
|
But seriously: memory gets a bad rap. I remember being forced to memorize countless meaningless facts in school.
|
||||||
|
|
||||||
|
But the problem isn't *memorization*, it's *meaninglessness*. If done well, memory *makes* meaning! There's a reason why, in Greek mythology, Memory was the mother of the Muses.
|
||||||
|
|
||||||
|
The Greeks were right. You may have heard that we should teach creativity & critical thinking *instead* of rote memorization – but cognitive scientists have shown that creativity & critical thinking *require* memorization! (Proof: imagine composing a poem or essay if you haven't even memorized your ABCs) Before we can *connect* the dots, we must first *collect* the dots.
|
||||||
|
|
||||||
|
But people use many strategies to learn, and most of them suck. In 2013, a team of cognitive scientists did a meta-study – a study of _200+ other studies_ – to find out how effective 10 study techniques were. Let's see if their findings match your expectations. (they didn't match *mine!*)
|
||||||
|
|
||||||
|
**Re-reading** to remember is...
|
||||||
|
|
||||||
|
[NOT EFFECTIVE]
|
||||||
|
|
||||||
|
A lot of students re-read text to study. It's hard, it's time-consuming, but at least it's... mostly useless. Oh well. On to the next:
|
||||||
|
|
||||||
|
**Highlighting** or underlining key material is...
|
||||||
|
|
||||||
|
[NOT EFFECTIVE]
|
||||||
|
|
||||||
|
That's right – despite re-reading & highlighting being the most common study techniques, they've both been proven to *NOT* work. Now, how about this:
|
||||||
|
|
||||||
|
**Spaced Practice**, spreading out your learning instead of cramming, is...
|
||||||
|
|
||||||
|
[HIGHLY EFFECTIVE!]
|
||||||
|
|
||||||
|
We're all guilty of cramming the night before the exam. And a month after the exam, you've forgotten everything. In contrast, spacing out your learning is easier _and_ helps you recall things better in the long term! Next:
|
||||||
|
|
||||||
|
**Visual Mnemonics**, making mental pictures for ideas you want to memorize, is...
|
||||||
|
|
||||||
|
[NOT EFFECTIVE]
|
||||||
|
|
||||||
|
This shocked me. Especially since I *love* visualization. But although imagery helps with learning foreign or technical vocabulary, it's not been shown to work over the long term like spaced practice does. Finally:
|
||||||
|
|
||||||
|
**Active Recall**, giving yourself quizzes, is...
|
||||||
|
|
||||||
|
[HIGHLY EFFECTIVE!]
|
||||||
|
|
||||||
|
You learn what you practice. The reason re-reading & highlighting is so ineffective is that you don't practice *recalling* answers, you practice *recognizing* answers.
|
||||||
|
|
||||||
|
There's a method that combines spaced practice and active recall. It's called **spaced repetition.** (I think "spaced recall" would be a better name, but alas) Compared to the most common study techniques, spaced repetition is easier, more effective, and *almost no schools tell you about it*.
|
||||||
|
|
||||||
|
// Note: Memory Palaces?
|
||||||
|
|
||||||
|
But I want to tell you about it. In the next 10 minutes, I'd like to help you start using spaced repetition *today*. Whether you're in school (my condolences) or learning on your own (my congratulations), I hope this little method can be the mother of your Muses.
|
||||||
|
|
||||||
|
But first... let's do a little active recall, shall we?
|
||||||
|
|
||||||
|
**TEST A:**
|
||||||
|
|
||||||
|
[3 Common But Ineffective Ways to Learn are...]
|
||||||
|
[Re-reading, Highlighting, Visual Mnemonics]
|
||||||
|
|
||||||
|
[2 Uncommon But Effective Ways to Learn are...]
|
||||||
|
[Spaced Practice, Active Recall]
|
||||||
|
|
||||||
|
[???]
|
||||||
|
[???]
|
||||||
|
|
||||||
|
## How & What
|
||||||
|
|
||||||
|
So, what, am I just recommending you use boring ol' flashcards? Well, yes. But they're flashcards... *on steroids!*
|
||||||
|
|
||||||
|
The problem with regular flashcards is that *you* decide when you see the cards. You may see cards you know as often as cards you don't. Or you may throw away cards you *think* you have in long-term memory, but really was only in short-term memory. The solution: have a shoebox tell you when to study.
|
||||||
|
|
||||||
|
**The Leitner Box** was invented by Sebastian Leitner in 1973, and was one of the first spaced repetition systems. It's a box. An actual box. (There's a digital version; we'll see that later) You can use this box to learn almost anything, but it's most popular in the language-learning community. Earlier this year, I started using this box to help me learn French – and in two *months*, the box taught me more than two *years* of Canadian school French.
|
||||||
|
|
||||||
|
Not bad for a shoebox! Here's how it works.
|
||||||
|
|
||||||
|
**To make the box:**
|
||||||
|
|
||||||
|
1. Get a box.
|
||||||
|
2. Make dividers to split the box into levels. (let's say 7 levels)
|
||||||
|
|
||||||
|
**To use the box:**
|
||||||
|
|
||||||
|
You put flashcards in the box.
|
||||||
|
|
||||||
|
// how flashcards work
|
||||||
|
|
||||||
|
Here's how often you should study (active recall) the cards at each level:
|
||||||
|
|
||||||
|
[pic]
|
||||||
|
|
||||||
|
Note that for each next level, the duration *doubles*. (spaced practice) Here's a 64-day calendar you can use:
|
||||||
|
|
||||||
|
[pic]
|
||||||
|
|
||||||
|
// (Note that you should review your higher-level cards _first_.)
|
||||||
|
|
||||||
|
But how do your cards get to each level? Well, first, new flashcards go into Level 1.
|
||||||
|
|
||||||
|
[pic]
|
||||||
|
|
||||||
|
When you review the cards in a single level, all the cards you got right go up to the next level, and all the cards you got wrong go... _back to Level 1._
|
||||||
|
|
||||||
|
[pic]
|
||||||
|
|
||||||
|
// (And if you're reviewing Level 1, just keep reviewing your incorrect cards until they all go up to Level 2)
|
||||||
|
|
||||||
|
This guarantees that you'll see cards you don't know often, and cards you do know only _just as you're about to forget them_. It's a system that strengthens your weakest points!
|
||||||
|
|
||||||
|
Here's a step-by-step simulation of what this looks like over several days:
|
||||||
|
|
||||||
|
[sim]
|
||||||
|
|
||||||
|
Now, for whatever reason, you may not like the idea of scribbling index cards to shove into a shoebox. Thankfully, there are also digital spaced repetition systems, the most popular of which is the free, open-source app, **Anki.**
|
||||||
|
|
||||||
|
(Anki uses a more sophisticated spaced-practice schedule than the Leitner box, but the core idea is the same.)
|
||||||
|
|
||||||
|
(ANTI-DISCLAIMER: I am *not* affiliated with Anki, I just genuinely think it's an awesome app. Even though I personally prefer the shoebox.)
|
||||||
|
|
||||||
|
Alright, now that I've sung the high praises of a shoebox, here's the catch:
|
||||||
|
|
||||||
|
// Only for declarative knowledge (but procedural skills can be aided)
|
||||||
|
|
||||||
|
// MEMORIZATION IS NOT ENOUGH (collect THEN connect. on the side, do stuff that uses the facts together)
|
||||||
|
|
||||||
|
**No matter what fancy learning techniques you use, if what you're learning feels meaningless, it won't work.** As I said earlier: the problem isn't memorization, it's meaninglessness. It's not knowing your *whats*, it's knowing your *why*.
|
||||||
|
|
||||||
|
So now, let's turn to you. What do you want to learn, and why?
|
||||||
|
|
||||||
|
∴ TEST B (3 cards)
|
||||||
|
|
||||||
|
[What does the "Forgetting Curve" look like?] []
|
||||||
|
|
||||||
|
[Leitner Box study schedule] []
|
||||||
|
|
||||||
|
[The most popular Spaced Repetition System app (as of Sep 2018) is...] [Anki (free, open source)]
|
||||||
|
|
||||||
|
## Using it for YOU
|
||||||
|
|
||||||
|
"Meaning" may seem like a fluffy woo-word, but it all comes down to the lament of bored students everywhere:
|
||||||
|
|
||||||
|
“When am I ever going to use this?”
|
||||||
|
|
||||||
|
Before I started using the shoebox, I had tried learning French with Duolingo. It's a great app. I'm not dissing it. It's free, it's polished, and it's helped lots of people. But not me. Learning a list of words (that I didn't choose) felt too removed from what I *really* wanted to do with the language, which was read French comics and be a French film snob.
|
||||||
|
|
||||||
|
But when I tried learning French again with the shoebox, I was forced to make my *own* list of words. And I got new vocabulary from the thing I *actually* cared about: Tintin comics! And the more words I learnt from Tintin, the better I got at reading Tintin. **The thing I actually cared about was both my source & destination.**
|
||||||
|
|
||||||
|
// pic
|
||||||
|
|
||||||
|
I think *that's* the trick. Not just for memorizing French, not even just for memorizing, but for *all learning*. It needs to go into (and ideally, also come from) things you love.
|
||||||
|
|
||||||
|
// pics: (examples involving memory)
|
||||||
|
// programming
|
||||||
|
// ukulele
|
||||||
|
// kinds of birds (thing itself)
|
||||||
|
|
||||||
|
That's why the consensus among spaced repetition enthusiasts is that, in almost all cases, **you should make your own cards.** That way, you have a chance to make your cards meaningful to *you*.
|
||||||
|
|
||||||
|
Speaking of which, it's time to make your own cards:
|
||||||
|
|
||||||
|
**WHAT do you want to learn?**
|
||||||
|
|
||||||
|
If you want to start small, here's some stuff you could memorize in a few days: Morse code, Braille, the ASL alphabet, US states & their capitals, the countries in your continent, etc
|
||||||
|
|
||||||
|
**WHY do you want to learn X?**
|
||||||
|
|
||||||
|
What do you want to use your knowledge *for?* (If it's for the sake of the knowledge itself, that's great! Write that.)
|
||||||
|
|
||||||
|
Examples: "Programming -> to make games"
|
||||||
|
|
||||||
|
**WHERE will you get your cards from?**
|
||||||
|
|
||||||
|
sadasdsaasdasas
|
||||||
|
|
||||||
|
But after all this, will spaced repetition be worth it for you? The results of spaced repetition only *really* show themselves after weeks or months. That's why spaced repetition isn't as popular compared to highlighting or re-reading: although it's more effective and just as easy, it *takes longer.* It's too bad we can't get a sneak peek into the future.
|
||||||
|
|
||||||
|
Except we can. Here's a simulation-based calculator to show you how much *you* can learn with a few minutes a day over a month or so...
|
||||||
|
|
||||||
|
// sim
|
||||||
|
|
||||||
|
And finally, just one final self-test:
|
||||||
|
|
||||||
|
∴ TEST A, B, C
|
||||||
|
|
||||||
|
## Get Started Right Now
|
||||||
|
|
||||||
|
Leitner Box:
|
||||||
|
|
||||||
|
* + Arts & Craftsy!
|
||||||
|
* + You can easily modify rules
|
||||||
|
* - Can't do sound
|
||||||
|
* - Not easily portable
|
||||||
|
|
||||||
|
Anki:
|
||||||
|
|
||||||
|
* + Most popular
|
||||||
|
* + Can do sound
|
||||||
|
* + Portable: laptop and mobile web versions
|
||||||
|
* + Can customize decks and rules
|
||||||
|
* - Clunky interface
|
||||||
|
|
||||||
|
Tinycards:
|
||||||
|
|
||||||
|
* + Very well-designed and polished
|
||||||
|
* + Easy to try out: no need to download or even sign up
|
||||||
|
* - Not open source
|
||||||
|
* - Less customizeable than Anki
|
||||||
|
|
||||||
|
GET STARTED
|
||||||
|
|
||||||
|
**Leitner Box:** go to a nearby office supply store and buy flashcards, and a box for the flashcards. (you can also buy dividers, or make your own) print out your study calendar
|
||||||
|
|
||||||
|
**Anki:** Download it here. Here's a tutorial by Fluent Forever
|
||||||
|
|
||||||
|
**Tinycards.** No need for buying supplies or download or even signing up at first. Go to X and try out a few flashcards!
|
||||||
|
|
||||||
|
In 1953, Henry Molaison had a brain surgery to get rid of his epilepsy. It also got rid of his ability to make new memories. From then on, he lived in a perpetual present. He died at 82. He stopped at 27.
|
||||||
|
|
||||||
|
Memory is not – should not – be late nights re-reading a textbook that feels heavier than your 2AM eyelids. Memory makes us human. Memory is the mother of all our muses. It's a shame that school has left too many of us with a Pavlovian fear-response of "learning" and "memorization". I hope this interactive comic helped ease some of those fears, and given you a simple, concrete way to take back your mind, and learn better. And you'll finally recognize memory for what it is:
|
||||||
|
|
||||||
|
It's a *superpower.*
|
|
@ -0,0 +1,57 @@
|
||||||
|
// LIMIT: 1000 WORDS
|
||||||
|
|
||||||
|
### Why & Science
|
||||||
|
|
||||||
|
! Why bother memorizing?
|
||||||
|
|
||||||
|
! Actually (Memory is mother of all muses)
|
||||||
|
|
||||||
|
! Spaced experiment
|
||||||
|
! Re-read/recall experiment
|
||||||
|
|
||||||
|
∴ Spaced Recall
|
||||||
|
|
||||||
|
∴ TEST A
|
||||||
|
|
||||||
|
### How & What
|
||||||
|
|
||||||
|
∴ Leitner Box (hint at digital version later)
|
||||||
|
|
||||||
|
!! Inconvenient? Anki!
|
||||||
|
|
||||||
|
! Limitations ! Workarounds
|
||||||
|
|
||||||
|
!! But is it worth it? The calculator, testimonials. Trial
|
||||||
|
|
||||||
|
∴ TEST B
|
||||||
|
|
||||||
|
### Using it for YOU
|
||||||
|
|
||||||
|
∴ What do you want to learn
|
||||||
|
|
||||||
|
!! Why?!
|
||||||
|
|
||||||
|
∴ What declarative facts do you need?
|
||||||
|
|
||||||
|
∴ What now? Leitner Box, Anki App, Tinycards
|
||||||
|
|
||||||
|
∴ TEST A, B, C
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Titles?**
|
||||||
|
|
||||||
|
How to Remember Forever
|
||||||
|
How to Remember Stuff
|
||||||
|
Memory, Mother of all Muses
|
||||||
|
What If Memory Was A Choice?
|
||||||
|
Remember Everything With This One Weird Trick
|
||||||
|
|
||||||
|
**THE GOAL: GET STARTED RIGHT NOW**
|
||||||
|
|
||||||
|
* model it
|
||||||
|
* observable results ( calculator, diverse models )
|
||||||
|
* compatible with life ( 20 minutes a day, or with Anki, on phone )
|
||||||
|
* relative advantage ( better than re-reading, even bare flashcards, school )
|
||||||
|
* simple to understand ( "use it or lose it" )
|
||||||
|
* trialable ( https://tinycards.duolingo.com/users/TinyGeo )
|
Binary file not shown.
After Width: | Height: | Size: 1.0 MiB |
Loading…
Reference in New Issue