-
Notifications
You must be signed in to change notification settings - Fork 7
/
codemaven-lessons.js
1450 lines (1399 loc) · 132 KB
/
codemaven-lessons.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright 2012 Geeky Ventures
// Each lesson has a tutor message, the code given at the start of the
// lesson, some hiddenCode that is not shown but defines some
// convenience functions and globals, a lessonSection that is
// displayed to give a sense of where you are and progress, which
// tutorImage to display, and a check that (when it exists) will
// display a little congratulatory "You got it!" the first time that
// regex appears in the student's code.
// code, if missing, will not change the code in the box.
// hiddenCode, lessonSection, and tutorImage all will, if missing, use
// the hiddenCode from the last lesson where it was defined.
// youGotItCheck, if missing, will just not display the congrats message.
// Of these, the message is always there and the code often there. Others
// are usually missing for most lessons.
var lessons =
[
// Draw box
{ message: "Hi, I'm Code Maven! (click on my words to see what's next)",
code: "c.fillRect(20, 20, 50, 75);\n",
lessonSection: "Getting Started",
tutorImage: 1,
hiddenCode: "var c = document.getElementById('pane').getContext('2d');\nfunction rgba(r,g,b,a) {return 'rgba('+[r,g,b,a].join(',')+')';}\nfunction rgb(r,g,b,a) {return 'rgb('+[r,g,b].join(',')+')';}\n\n"
// Hide the access to the canvas, which is ugly, giving instead c
// rgba() is a string, but students sometimes want to use it as
// a function. Let that quietly succeed, not worth confusing them
// on that.
},
{ message: "You're going to learn some programming! (click again)"
},
{ message: "Below me, on the left, is Javascript code, on the right, what it does."
},
{ message: "You can use fillRect() to draw a box. See the number 50? Can you change that to 150?",
code: "c.fillRect(20, 20, 50, 75);\n",
youGotItCheck: "150",
},
{ message: "That made the box wider. What do you think the other numbers do? Try changing them.",
lessonSection: "Parameters and Drawing",
},
{ message: "Did you figure it out? The numbers are how far from the left side to draw the box, how far from the top, how wide to draw the box, and how tall. So, it's <span class=tutor-code>c.fillRect(<i>left, top, width, height</i>);</span>",
tutorImage: 4,
},
{ message: "Try changing all the numbers! Play with it!",
tutorImage: 5,
},
{ message: "Two boxes. Can you change the numbers to make them bigger?",
code: "c.fillRect(20, 20, 50, 75);\nc.fillRect(100, 100, 60, 80);\n",
tutorImage: 3,
},
{ message: "Can you change the numbers to move them around?",
},
{ message: "How about something a little more challenging. Can you change the numbers so that one box covers up the other?",
tutorImage: 6,
},
// Variables
{ message: "Let's work with square boxes. Square boxes have the same width and height.",
code: "c.fillRect(30, 20, 80, 80);\n",
tutorImage: 1,
},
{ message: "Hey, look, this does the same thing!",
code: "var size = 80;\nc.fillRect(30, 20, size, size);\n",
lessonSection: "Variables",
tutorImage: 2,
},
{ message: "Why does that do the same thing? Any ideas?",
tutorImage: 4,
},
{ message: "var means variable. What we did is create a variable, called it <span class=tutor-code>size</span>, set it equal to 80, and now we can use <span class=tutor-code>size</span> where we would have used 80. Do you know why we might want that?",
code: "var size = 80;\nc.fillRect(30, 20, size, size);\n"
},
{ message: "What happens if you change 80 to 220?",
tutorImage: 1,
},
{ message: "And look! Wider and taller box, both at the same time!"
},
{ message: "What would you change to move the box around?"
},
{ message: "Can you make the box very small?"
},
{ message: "What happens if you make <span class=tutor-code>size</span> equal to 0? Or to something really big like 5000?"
},
// More boxes
{ message: "I'm going to make two boxes. Like this.",
code: "var size = 80;\nc.fillRect(20, 20, size, size);\nc.fillRect(90, 90, size, size);\n",
tutorImage: 6,
},
{ message: "Both boxes are the same size squares because they are both using the variable named <span class=tutor-code>size</span> for width and height."
},
{ message: "You can change what <span class=tutor-code>size</span> is equal to. That will grow or shrink both boxes at once! Try changing 80 to 180!",
tutorImage: 4,
},
{ message: "Let's add more boxes. Here are three boxes! Try changing size or move the boxes around.",
code: "var size = 80;\nc.fillRect(20, 20, size, size);\nc.fillRect(80, 80, size, size);\nc.fillRect(140, 140, size, size);\n",
tutorImage: 1,
},
{ message: "Even more boxes! Try changing size now, maybe something smaller like 10 or 50?",
code: "var size = 80;\nc.fillRect(20, 20, size, size);\nc.fillRect(60, 60, size, size);\nc.fillRect(100, 100, size, size);\nc.fillRect(140, 140, size, size);\nc.fillRect(180, 180, size, size);\n",
tutorImage: 5,
},
// Two colored boxes
{ message: "Look, color! That's fun, isn't it?",
code: "var size = 80;\n\nc.fillStyle = \"blue\";\nc.fillRect(20, 20, size, size);\n",
lessonSection: "Color",
tutorImage: 4,
},
{ message: "Many colors work. Try \"green\" or \"purple\" or \"gold\"!",
tutorImage: 2,
},
{ message: "How about two pink boxes?",
code: "var size = 80;\n\nc.fillStyle =\"pink\";\nc.fillRect(20, 20, size, size);\nc.fillRect(90, 90, size, size);\n",
tutorImage: 3,
},
{ message: "Here is one pink and one silver.",
code: "var size = 80;\n\nc.fillStyle =\"pink\";\nc.fillRect(20, 20, size, size);\nc.fillStyle =\"silver\";\nc.fillRect(90, 90, size, size);\n"
},
{ message: "Can you make the silver box gold instead? And the pink one blue instead?",
code: "var size = 80;\n\nc.fillStyle =\"pink\";\nc.fillRect(20, 20, size, size);\nc.fillStyle =\"silver\";\nc.fillRect(90, 90, size, size);\n",
tutorImage: 6,
},
{ message: "Here are two boxes that are the same color again. Now can you make the two boxes different colors?",
code: "var size = 80;\n\nc.fillStyle =\"green\";\nc.fillRect(20, 20, size, size);\n\nc.fillRect(90, 90, size, size);\n",
tutorImage: 1,
},
{ message: "Here is one version, red and blue.",
code: "var size = 80;\n\nc.fillStyle =\"red\";\nc.fillRect(20, 20, size, size);\nc.fillStyle =\"blue\";\nc.fillRect(80, 80, size, size);\n"
},
{ message: "Red, green, and blue. That's nice.",
code: "var size = 80;\n\nc.fillStyle =\"red\";\nc.fillRect(20, 20, size, size);\nc.fillStyle =\"lime\";\nc.fillRect(80, 80, size, size);\nc.fillStyle =\"blue\";\nc.fillRect(140, 140, size, size);\n"
},
{ message: "Huh, this does the same thing? What could rgb() be?",
code: "var size = 80;\n\nc.fillStyle =\"rgb(255, 0, 0)\";\nc.fillRect(20, 20, size, size);\nc.fillStyle =\"rgb(0, 255, 0)\";\nc.fillRect(80, 80, size, size);\nc.fillStyle =\"rgb(0, 0, 255)\";\nc.fillRect(140, 140, size, size);\n",
tutorImage: 4,
},
{ message: "Try changing any of the zeroes to 200 or so. And try changing any of the 255 to 0. What do the numbers seem to do?",
code: "var size = 80;\n\nc.fillStyle =\"rgb(255, 0, 0)\";\nc.fillRect(20, 20, size, size);\nc.fillStyle =\"rgb(0, 255, 0)\";\nc.fillRect(80, 80, size, size);\nc.fillStyle =\"rgb(0, 0, 255)\";\nc.fillRect(140, 140, size, size);\n",
tutorImage: 6,
},
{ message: "Did you figure it out? rgb() refers to red, green, and blue. The numbers go from 0 to 255. So, <span class=tutor-code>rgb(0, 255, 0)</span> means no red or blue, but all the green you got!",
tutorImage: 1,
},
{ message: "You can make lots of colors this way if you change some of the numbers. Try it!",
tutorImage: 5,
},
{ message: "Here is a fun game. Can you make these two boxes the same color?",
code: "var size = 80;\n\nc.fillStyle =\"rgb(0, 0, 0)\";\nc.fillRect(20, 20, size, size);\nc.fillStyle =\"red\";\nc.fillRect(80, 80, size, size);\n",
lessonSection: "Quiz: Color",
tutorImage: 2,
},
{ message: "Can you make these both blue?",
code: "var size = 80;\n\nc.fillStyle =\"rgb(0, 0, 0)\";\nc.fillRect(20, 20, size, size);\nc.fillStyle =\"blue\";\nc.fillRect(80, 80, size, size);\n"
},
{ message: "Can you figure out what the rgb() numbers should be to make these both yellow?",
code: "var size = 80;\n\nc.fillStyle =\"rgb(255, 0, 0)\";\nc.fillRect(20, 20, size, size);\nc.fillStyle =\"yellow\";\nc.fillRect(80, 80, size, size);\n",
tutorImage: 4,
},
{ message: "Can you figure out what the rgb() numbers should be to make these both teal?",
code: "var size = 80;\n\nc.fillStyle =\"rgb(0, 0, 128)\";\nc.fillRect(20, 20, size, size);\nc.fillStyle =\"teal\";\nc.fillRect(80, 80, size, size);\n"
},
{ message: "Okay, this one is really hard. Can you make these two exactly match? Can you figure out what the rgb() numbers should be to make these both crimson? Don't worry if you don't get it exactly, just see how close you can get!",
code: "var size = 80;\n\nc.fillStyle =\"rgb(0, 0, 0)\";\nc.fillRect(20, 20, size, size);\nc.fillStyle =\"crimson\";\nc.fillRect(80, 80, size, size);\n",
tutorImage: 2,
},
{ message: "Whew, that's a hard one! How close did you get?",
code: "var size = 80;\n\nc.fillStyle =\"rgb(220, 20, 60)\";\nc.fillRect(20, 20, size, size);\nc.fillStyle =\"crimson\";\nc.fillRect(80, 80, size, size);\n",
tutorImage: 3,
},
{ message: "If you want to try others, put \"olive\", \"purple\", \"aqua\", or any other color you can think of as the color for the second box, then try to find the rgb numbers for the first box that match it!",
},
// rgba
{ message: "rgba() is a fun version of rgb(). See what this does? It's got one more number at the end that's 0.5. What is that last number doing? Try changing the 0.5 to 0.1. Or to 0.8. What does it do?",
code: "var size = 80;\n\nc.fillStyle =\"rgba(255, 0, 0, 0.5)\";\nc.fillRect(20, 20, size, size);\nc.fillStyle =\"rgba(0, 0, 255, 0.5)\";\nc.fillRect(80, 80, size, size);\n",
lessonSection: "Color and Transparency",
tutorImage: 6,
},
{ message: "The a in rgba means alpha (how transparent the box is) from 0.0 (invisible) to 1.0 (solid)",
tutorImage: 1,
},
{ message: "You can do pretty cool things with rgba(). Look at this! Try changing some of the 0.5 alphas to 0.2 or 0.8!",
code: "var size = 80;\n\nc.fillStyle =\"rgba(255, 0, 0, 0.5)\";\nc.fillRect(20, 20, size, size);\nc.fillStyle =\"rgba(0, 255, 0, 0.5)\";\nc.fillRect(80, 80, size, size);\nc.fillStyle =\"rgba(0, 0, 255, 0.5)\";\nc.fillRect(140, 140, size, size);\n",
tutorImage: 2,
},
{ message: "And try changing the size from 80 to 180! You can get some fun effects when the colors overlap. Play with it!",
tutorImage: 5,
},
// Operators and assignment
{ message: "Let's go back to variables. Variables make it easier to change what our code does. Let's use <span class=tutor-code>offset</span> for how far the first box is from the left and top. Can you replace the <span class=tutor-code>20</span> numbers with <span class=tutor-code>offset</span>?",
code: "var offset = 30;\nvar size = 80;\nc.fillStyle =\"lime\";\nc.fillRect(20, 20, size, size);\nc.fillRect(90, 90, size, size);\n",
lessonSection: "Operators and Assignment",
tutorImage: 1,
},
{ message: "Now try changing what <span class=tutor-code>offset</span> is equal to from 30 to 50. See how you can move the first box?",
code: "var offset = 30;\nvar size = 80;\nc.fillStyle =\"lime\";\nc.fillRect(offset, offset, size, size);\nc.fillRect(90, 90, size, size);\n"
},
{ message: "Variables can be set to new values. See how offset is set to 100 before being used by the second box?",
code: "var offset = 30;\nvar size = 80;\nc.fillStyle =\"lime\";\nc.fillRect(offset, offset, size, size);\noffset = 100;\nc.fillRect(offset, offset, size, size);\n",
tutorImage: 6,
},
{ message: "Try moving the second box. Change the 100 to 50 or 150.",
},
{ message: "Try moving the first box too. Remember what do you have to change to move the first box?",
tutorImage: 3,
},
{ message: "Can you put the boxes on top of each other so they overlap and it looks like just one box?",
tutorImage: 4,
},
{ message: "You can also add numbers to a variable. See what this is doing? It changes offset with <span class=tutor-code>offset = offset + 50</span>",
code: "var offset = 30;\nvar size = 80;\nc.fillStyle =\"lime\";\nc.fillRect(offset, offset, size, size);\noffset = offset + 50;\nc.fillRect(offset, offset, size, size);\n",
tutorImage: 1,
},
{ message: "<span class=tutor-code>offset = offset + 50</span> means take offset, add 50 to it, then make offset equal that now. In other words, make offset 50 more than it used to be."
},
{ message: "How about you try it? Can you replace both the 90 numbers used for the (left, top) of the second box with a variable? Hint: Either create a new variable or change offset and then use offset.",
code: "var offset = 30;\nvar size = 80;\nc.fillStyle =\"lime\";\nc.fillRect(offset, offset, size, size);\n\nc.fillRect(90, 90, size, size);\n",
lessonSection: "Quiz: Variables and Operators",
tutorImage: 2,
},
{ message: "Here's one way. It adds 60 to whatever <span class=tutor-code>offset</span> was before, and makes that the new value of <span class=tutor-code>offset</span>. Okay, now try changing what <span class=tutor-code>offset</span> is equal to at the beginning from 30 to 80. See what happens?",
code: "var offset = 30;\nvar size = 80;\nc.fillStyle =\"lime\";\nc.fillRect(offset, offset, size, size);\noffset = offset + 60;\nc.fillRect(offset, offset, size, size);\n",
tutorImage: 3,
},
{ message: "You can move both boxes together! Change the offset and move 'em around!",
tutorImage: 5,
},
{ message: "This looks the same, but it's a little different. <span class=tutor-code>*</span> means multiply. <span class=tutor-code>2 * 2</span> would be 4. So, <span class=tutor-code>offset = offset * 3</span> means make offset three times bigger.",
code: "var offset = 30;\nvar size = 80;\nc.fillStyle =\"lime\";\nc.fillRect(offset, offset, size, size);\noffset = offset * 3;\nc.fillRect(offset, offset, size, size);\n",
lessonSection: "More Operators",
tutorImage: 3,
},
{ message: "<span class=tutor-code>*</span> means multiply and <span class=tutor-code>/</span> means divide. So, when we set size equal to <span class=tutor-code>20 * 6 / 2 + 50 - 10</span>, that's just a complicated way of saying make size equal to 100. Does that make sense?",
code: "var size = 20 * 6 / 2 + 50 - 10;\nvar offset = 30;\nc.fillStyle =\"lime\";\nc.fillRect(offset, offset, size, size);\noffset = offset * 3;\nc.fillRect(offset, offset, size, size);\n"
},
// Conditionals and comparisons
{ message: "We can also compare numbers. See this code? We will only draw a second box when <span class=tutor-code>size</span> is less than 80.",
code: "var size = 50;\n\nc.fillRect(20, 20, size, size);\nif (size < 80) {\n c.fillRect(100, 100, size, size);\n}\n",
lessonSection: "If and Comparisons",
tutorImage: 4,
},
{ message: "Try changing size to 150. See what happens? Try changing size to 79. Then change it to 80. Neat, isn't it!",
tutorImage: 3,
},
// Three colored boxes
{ message: "Okay, let's see what you know! Here are two boxes. Can you add a third box (also offset by 60 from the previous box)?",
code: "var size = 80;\nvar offset = 20;\n\nc.fillStyle =\"lime\";\n\nc.fillRect(offset, offset, size, size);\noffset = offset + 60;\nc.fillRect(offset, offset, size, size);\n",
lessonSection: "Quiz: Variables and Color",
tutorImage: 1,
},
{ message: "Here's one way to do it. Try changing the size or offset!",
code: "var size = 80;\nvar offset = 20;\n\nc.fillStyle =\"lime\";\n\nc.fillRect(offset, offset, size, size);\noffset = offset + 60;\nc.fillRect(offset, offset, size, size);\noffset = offset + 60;\nc.fillRect(offset, offset, size, size);\n",
tutorImage: 3,
},
{ message: "Remember rgba()? Don't you like what happens when transparent shapes overlap? This is nice, but it would be even better with different colors. Can you change this so the three boxes have different colors?",
code: "var size = 80;\nvar offset = 20;\n\nc.fillStyle =\"rgba(0, 0, 255, 0.5)\";\nc.fillRect(offset, offset, size, size);\noffset = offset + 60;\nc.fillStyle =\"rgba(0, 0, 255, 0.5)\";\nc.fillRect(offset, offset, size, size);\noffset = offset + 60;\nc.fillStyle =\"rgba(0, 0, 255, 0.5)\";\nc.fillRect(offset, offset, size, size);\n",
tutorImage: 4,
},
{ message: "Here's one way. Awesome! Try changing <span class=tutor-code>size</span> to make them bigger! Or change <span class=tutor-code>offset</span> to move them around! Or play with the red, blue, green, and alpha values to try different colors and transparencies!",
code: "var size = 80;\nvar offset = 20;\n\nc.fillStyle =\"rgba(0, 0, 255, 0.5)\";\nc.fillRect(offset, offset, size, size);\noffset = offset + 60;\nc.fillStyle =\"rgba(0, 255, 0, 0.5)\";\nc.fillRect(offset, offset, size, size);\noffset = offset + 60;\nc.fillStyle =\"rgba(255, 0, 0, 0.5)\";\nc.fillRect(offset, offset, size, size);\n",
tutorImage: 5,
},
// For loop to avoid repeated code
{ message: "You know, I really don't like writing code more than once. Look at this program. There is lots of the same code in this program. See how the exact same thing, <span class=tutor-code>c.fillRect(offset, offset, size, size)</span>, appears on three different lines? And how we add 40 to <span class=tutor-code>offset</span> every time before we draw another box?",
code: "var size = 80;\nvar offset = 20;\n\nc.fillStyle =\"blue\";\n\nc.fillRect(offset, offset, size, size);\noffset = offset + 40;\nc.fillRect(offset, offset, size, size);\noffset = offset + 40;\nc.fillRect(offset, offset, size, size);\n",
lessonSection: "For Loops",
tutorImage: 1,
},
{ message: "Let me show you something. This is called a <span class=tutor-code>for</span> loop. It repeats code without writing it more than once. Isn't that great? Try changing the 3 to 5!",
code: "var size = 80;\nvar offset = 20;\n\nc.fillStyle =\"blue\";\nfor (var i = 0; i < 3; i = i + 1) {\n c.fillRect(offset, offset, size, size);\n offset = offset + 40;\n}\n",
},
{ message: "For loops have three parts, where to start (<span class=tutor-code>i = 0</span>), when to keep going (<span class=tutor-code>i < 5</span>), and how much to change each time (<span class=tutor-code>i = i + 1</span>). Can you make ten boxes?",
code: "var size = 80;\nvar offset = 20;\n\nc.fillStyle =\"blue\";\nfor (var i = 0; i < 5; i = i + 1) {\n c.fillRect(offset, offset, size, size);\n offset = offset + 40;\n}\n",
},
{ message: "Oh no! They don't fit! How can you make ten boxes fit?",
code: "var size = 80;\nvar offset = 20;\n\nc.fillStyle =\"blue\";\nfor (var i = 0; i < 10; i = i + 1) {\n c.fillRect(offset, offset, size, size);\n offset = offset + 40;\n}\n",
tutorImage: 4,
},
{ message: "Here is one way!",
code: "var size = 80;\nvar offset = 20;\n\nc.fillStyle =\"blue\";\nfor (var i = 0; i < 10; i = i + 1) {\n c.fillRect(offset, offset, size, size);\n offset = offset + 10;\n}\n",
tutorImage: 2,
},
{ message: "Smaller boxes also fit. Try playing with size, offset, and changing the number of boxes!",
code: "var size = 20;\nvar offset = 20;\n\nc.fillStyle =\"blue\";\nfor (var i = 0; i < 10; i = i + 1) {\n c.fillRect(offset, offset, size, size);\n offset = offset + 10;\n}\n",
},
// Lots of colored boxes
{ message: "Let's play with rgba() again! What's this doing?",
code: "var size = 50;\nvar offset = 20;\n\nfor (var i = 0; i < 10; i = i + 1) {\n var b = i * 25;\n var rgba = \"rgba(0, 0, \" + b + \", 0.5)\";\n c.fillStyle = rgba;\n c.fillRect(offset, offset, size, size);\n offset = offset + 20;\n}\n",
lessonSection: "Fun with For Loops",
tutorImage: 6,
},
{ message: "Complicated! That means use 0 blue light for the first box, 25 for the second, then 50, 75, 100..."
},
{ message: "Here is another one, this one changing alpha! Isn't that fantastic?",
code: "var size = 50;\nvar offset = 20;\n\nfor (var i = 0.1; i < 0.5; i = i + 0.05) {\n var rgba = \"rgba(0, 0, 255, \" + i + \")\";\n c.fillStyle = rgba;\n c.fillRect(offset, offset, size, size);\n offset = offset + 20;\n}\n",
tutorImage: 5,
},
// Quiz
{ message: "Can you show me what you learned? Draw me a square! Hint: Remember <span class=tutor-code>c.fillRect(<i>left, top, width, height</i>);</span> to draw a box.",
code: "",
lessonSection: "Quiz: Write Code Yourself",
tutorImage: 2,
},
{ message: "Can you add another square?",
tutorImage: 4,
},
{ message: "Now can you make the second square red? (Hint: <span class=tutor-code>c.fillStyle = \"blue\";</span> before fillRect() will make the rect blue)",
tutorImage: 3,
},
{ message: "Great! Did you get something like this? If not, try playing with the numbers a bit to see how it works!",
code: "c.fillRect(10, 10, 20, 20);\nc.fillStyle = \"red\";\nc.fillRect(50, 50, 100, 100);\n",
tutorImage: 2,
},
// Draw line
{ message: "Let's do something different now. Let's play with lines. This is the code you use to draw lines in Javascript. What do you think moveTo() does? lineTo()?",
code: "c.beginPath();\nc.moveTo(30, 20);\nc.lineTo(120, 40);\nc.stroke();\n",
lessonSection: "Lines",
tutorImage: 1,
},
{ message: "Let's play with it. Try changing 20 to 50. Try changing the other numbers too. Can you figure out what moveTo() and lineTo() do? How does this all work?",
code: "c.beginPath();\nc.moveTo(30, 20);\nc.lineTo(120, 40);\nc.stroke();\n",
},
{ message: "<span class=tutor-code>moveTo(<i>left, top</i>)</span> moves the pen to a spot without drawing. <span class=tutor-code>lineTo(<i>left, top</i>)</span> draws a line from wherever the pen is to a spot.",
tutorImage: 6,
},
{ message: "Here is what happens if we do a second lineTo(). The second line starts from where the first ended.",
code: "c.beginPath();\nc.moveTo(30, 20);\nc.lineTo(120, 40);\nc.lineTo(120, 150);\nc.stroke();\n",
tutorImage: 3,
},
{ message: "So, we're drawing a path, a trail of lines all connected together. We start the path with beginPath() and draw everything with stroke().",
tutorImage: 4,
},
{ message: "If we put a moveTo() before the second lineTo(), we'll move the pen without drawing. See?",
code: "c.beginPath();\nc.moveTo(30, 20);\nc.lineTo(120, 40);\nc.moveTo(120, 60);\nc.lineTo(120, 150);\nc.stroke();\n",
tutorImage: 6,
},
{ message: "You try it! Can you add another line?. Put a second c.lineTo() after the first. Make your second line go to (50, 115).",
code: "c.beginPath();\nc.moveTo(100, 30);\nc.lineTo(150, 115);\n\nc.stroke();\n",
tutorImage: 1,
},
{ message: "Now can you make a triangle?",
code: "c.beginPath();\nc.moveTo(100, 30);\nc.lineTo(150, 115);\nc.lineTo(50, 115);\n\nc.stroke();\n",
tutorImage: 4,
},
{ message: "Here is one way, a triangle!",
code: "c.beginPath();\nc.moveTo(100, 30);\nc.lineTo(150, 115);\nc.lineTo(50, 115);\nc.lineTo(100, 30);\nc.stroke();\n",
tutorImage: 2,
},
{ message: "Huh, an orange triangle. I think I'd like blue better. Can you make it blue?",
code: "c.beginPath();\nc.strokeStyle = \"orange\";\nc.moveTo(100, 30);\nc.lineTo(150, 115);\nc.lineTo(50, 115);\nc.lineTo(100, 30);\nc.stroke();\n",
tutorImage: 3,
},
{ message: "Oh no, I was trying to make two triangles, but forgot a moveTo(). See what happened?",
code: "c.beginPath();\nc.moveTo(100, 30);\nc.lineTo(150, 115);\nc.lineTo(50, 115);\nc.lineTo(100, 30);\n\nc.lineTo(250, 215);\nc.lineTo(150, 215);\nc.lineTo(200, 130);\nc.stroke();\n",
tutorImage: 4,
},
{ message: "That's not good. Can you add <span class=tutor-code>c.moveTo(200, 130);</span> to make it two separate triangles?",
tutorImage: 1,
},
{ message: "I think I'm going to want a lot of triangles. This for loop draws two triangles.",
code: "c.beginPath();\nfor (var i = 30; i <= 60; i = i + 30) {\n c.moveTo(70 + i, i);\n c.lineTo(120 + i, 85 + i);\n c.lineTo(20 + i, 85 + i);\n c.lineTo(70 + i, i);\n}\nc.stroke();\n",
lessonSection: "Lines and Loops",
tutorImage: 6,
},
{ message: "This for loop starts at 30 and increases by 30 every time. So, until it is told to stop, it counts 30, 60, 90, 120... Right now, the for loop is told to stop at 60."
},
{ message: "So, can you make this for loop draw three triangles? Five?",
tutorImage: 4,
},
{ message: "Can you make more triangles by changing how the for loop goes up? What if it counted by 10 each time, so it would count 30, 40, 50, 60... Can you do try that?",
tutorImage: 1,
},
{ message: "Look at this! Forty green triangles!",
code: "c.strokeStyle = \"green\";\nc.beginPath();\nfor (var i = 10; i <= 210; i = i + 5) {\n c.moveTo(70 + i, i);\n c.lineTo(120 + i, 85 + i);\n c.lineTo(20 + i, 85 + i);\n c.lineTo(70 + i, i);\n}\nc.stroke();\n",
tutorImage: 5,
},
{ message: "Play with it more! Can you change the color? Can you make even more triangles?",
tutorImage: 2,
},
// Draw a box using lines
{ message: "Let me show you something. Here are two boxes, one using strokeRect(), the other using four lines.",
code: "var size = 100;\n// First box\nc.strokeRect(30, 30, size, size);\n// Second box\nc.beginPath();\nc.moveTo(80, 80);\nc.lineTo(80 + size, 80);\nc.lineTo(80 + size, 80 + size);\nc.lineTo(80, 80 + size);\nc.lineTo(80, 80);\nc.stroke();\n",
lessonSection: "Coding It Easy",
tutorImage: 6,
},
{ message: "It takes a lot more code to draw it with lines. Can you add a third box?",
code: "var size = 100;\n// First box\nc.strokeRect(30, 30, size, size);\n// Second box\nc.beginPath();\nc.moveTo(80, 80);\nc.lineTo(80 + size, 80);\nc.lineTo(80 + size, 80 + size);\nc.lineTo(80, 80 + size);\nc.lineTo(80, 80);\nc.stroke();\n// Third box?\n",
tutorImage: 3,
},
{ message: "Functions that do a lot of work for you, like strokeRect(), make coding easier. Here are three boxes with all of them using strokeRect(). Can you add a fourth?",
code: "var size = 100;\n\nc.strokeRect(30, 30, size, size);\nc.strokeRect(80, 80, size, size);\nc.strokeRect(130, 130, size, size);\n",
tutorImage: 1,
},
{ message: "What if we want even more boxes? I guess we'd have to keep adding lines with c.strokeRect()? Or is there an easier way?",
lessonSection: "Avoid Repeating Code",
tutorImage: 4,
},
{ message: "I know! Let's use a for loop! Now can you make more boxes?",
code: "var size = 100;\n\nfor (var x = 30; x <= 80; x = x + 50) {\n c.strokeRect(x, x, size, size);\n}\n",
tutorImage: 2,
},
{ message: "Can you make more than ten boxes? And still fit all of them on the screen?",
tutorImage: 6,
},
{ message: "Is it easier now to make more boxes? Why is that?",
code: "var num = 3;\nvar size = 100;\nvar offset = 20;\n\nfor (var i = 1; i <= num; i = i + 1) {\n c.strokeRect(i * offset, i * offset, size, size);\n}\n",
lessonSection: "Variables Make It Easy Too",
tutorImage: 4,
},
{ message: "This works by having <span class=tutor-code>i</span> count up 1, 2, 3... then putting each box's (left, top) at <span class=tutor-code>i * offset</span>. Since offset is 20, the top left of the first box is (20, 20), the second is at (40, 40), then (60, 60) ...",
tutorImage: 6,
},
{ message: "Try changing <span class=tutor-code>num</span>, <span class=tutor-code>size</span>, and <span class=tutor-code>offset</span>. See what happens? Play with it!",
tutorImage: 5,
},
{ message: "It's often good to have variables that control things in your code (like <span class=tutor-code>num</span>, <span class=tutor-code>size</span>, and <span class=tutor-code>offset</span>) together so they are easy to change.",
tutorImage: 1,
},
// Quiz
{ message: "Can you show me some of what you know? Draw a box. <br>Hint: Remember <span class=tutor-code>c.strokeRect(<i>left, top, width, height</i>);</span>",
code: "",
lessonSection: "Quiz: For Loops",
tutorImage: 3,
},
{ message: "Now can you draw four boxes? Hint: Making four boxes is easy using for. Remember, a for loop look like this: <span class=tutor-code>for (var i = 0; i < 3; i += 1) {</span>",
tutorImage: 1,
},
{ message: "Can you make it so you can change the size of all your boxes all at once? Hint: To make a variable named size set to 50, you use <span class=tutor-code>var size = 50;</span>",
tutorImage: 3,
},
{ message: "Wow, you're learning to program! Lots of ways you could do this, but did you get something like this? If not, try changing it, figure out how it works!",
code: "var size = 50;\nvar num = 4;\n\nfor (var i = 0; i < num; i += 1) {\n var x = 80 + 30 * i;\n var y = 80 + 10 * i;\n c.strokeRect(x, y, size, size);\n}\n",
tutorImage: 5,
},
// Functions and squares
{ message: "Copied code that says the same thing as other code is hard to change. You know how for loops avoid repeating the same code, right? Functions also avoid repeating. Let me show you how to write your own functions.",
code: "// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// All work and no play\n// makes Code Maven a dull girl\n// Let's play!\n",
lessonSection: "Your Own Functions",
tutorImage: 1,
},
{ message: "I just made a new function, strokeSquare(). It is just like strokeRect(), but draws squares.",
code: "function strokeSquare(left, top, size) {\n c.strokeRect(left, top, size, size);\n}\n\nstrokeSquare(30, 100, 50);\nstrokeSquare(10, 10, 200);\nstrokeSquare(50, 50, 10);\n",
tutorImage: 6,
},
{ message: "strokeSquare() uses strokeRect() to draw a rectangle with the same width and height. See how it works? strokeSquare() only takes a size, not both a width and height, and then tells strokeRect() that both the width and height are size.",
tutorImage: 1,
},
{ message: "So, the first square starts at (30, 100) and then has a height and width of 50.",
},
{ message: "Can you add a fourth square below the others using strokeSquare()?",
tutorImage: 4,
},
{ message: "Let's talk about how this works in a little more detail. <span class=tutor-code>function strokeSquare</span> creates a new function called strokeSquare. <span class=tutor-code>(left, top, size)</span> are the <i>parameters</i> to the function and act like variables inside the function. The code inside the brackets (the <span class=tutor-code>{</span> and <span class=tutor-code>}</span>) is the <i>function body</i> contains all the code that is run every time you call the function.",
tutorImage: 6,
},
{ message: "<span class=tutor-code>strokeSquare(30, 100, 50);</span> calls the code in the function body of strokeSquare, with <span class=tutor-code>left</span> set equal to 30, <span class=tutor-code>top</span> to 100, and <span class=tutor-code>size</span> to 50. That code uses c.strokeRect() to draw us a square. See how that works?",
code: "function strokeSquare(left, top, size) {\n c.strokeRect(left, top, size, size);\n}\n\nstrokeSquare(30, 100, 50);\n",
},
{ message: "Making your own functions is really useful. It makes it easier to program.",
},
// Draw a star
{ message: "Now for something completely different. A star! I like that! Do you like stars too? If you want, you can change some of the numbers and mess it up! Try it!",
code: "c.beginPath();\nc.moveTo(50, 100);\nc.lineTo(150, 100);\nc.lineTo(65, 150);\nc.lineTo(100, 60);\nc.lineTo(135, 150);\nc.lineTo(50, 100);\nc.stroke();\n",
lessonSection: "More Lines",
tutorImage: 1,
},
{ message: "Wait, stars aren't green. Can you make it yellow or gold?",
code: "c.strokeStyle = \"green\";\nc.beginPath();\nc.moveTo(50, 100);\nc.lineTo(150, 100);\nc.lineTo(65, 150);\nc.lineTo(100, 60);\nc.lineTo(135, 150);\nc.lineTo(50, 100);\nc.stroke();\n",
tutorImage: 4,
},
{ message: "A solid gold star! Did you know you can do that? What's different? How did I make the star solid?",
code: "c.fillStyle = \"gold\";\nc.beginPath();\nc.moveTo(50, 100);\nc.lineTo(150, 100);\nc.lineTo(65, 150);\nc.lineTo(100, 60);\nc.lineTo(135, 150);\nc.lineTo(50, 100);\nc.fill();\n",
tutorImage: 5,
},
{ message: "Just like <span class=tutor-code>c.stroke()</span> draws lines in whatever color <span class=tutor-code>c.strokeStyle</span> is set to, <span class=tutor-code>c.fill()</span> fills a shape with whatever color <span class=tutor-code>c.fillStyle</span> is. It's that fun?",
tutorImage: 2,
},
{ message: "Variables make it easy to change and add more stars. Try changing <span class=tutor-code>x</span>, <span class=tutor-code>y</span>, and <span class=tutor-code>size</span> (or anything else)!",
code: "c.fillStyle = \"gold\";\n\nvar x = 50;\nvar y = 100;\nvar size = 100;\n\nc.beginPath();\nc.moveTo(x, y);\nc.lineTo(x + size, y);\nc.lineTo(x + size * 0.15, y + size * 0.5);\nc.lineTo(x + size / 2, y - size * 0.4);\nc.lineTo(x + size * 0.85, y + size * 0.5);\nc.lineTo(x, y);\nc.fill();\n",
tutorImage: 1,
},
{ message: "Here are two stars, but look at all that code! Most of it is the same too, just copied, repeating over and over. That doesn't seem like the best way to do two stars. What can we do?",
code: "c.fillStyle = \"gold\";\n\nvar x = 50;\nvar y = 100;\nvar size = 100;\n\nc.beginPath();\nc.moveTo(x, y);\nc.lineTo(x + size, y);\nc.lineTo(x + size * 0.15, y + size * 0.5);\nc.lineTo(x + size / 2, y - size * 0.4);\nc.lineTo(x + size * 0.85, y + size * 0.5);\nc.lineTo(x, y);\nc.fill();\nx += 100;\ny += 50;\nc.beginPath();\nc.moveTo(x, y);\nc.lineTo(x + size, y);\nc.lineTo(x + size * 0.15, y + size * 0.5);\nc.lineTo(x + size / 2, y - size * 0.4);\nc.lineTo(x + size * 0.85, y + size * 0.5);\nc.lineTo(x, y);\nc.fill();\n",
tutorImage: 4,
},
{ message: "Functions! It's fillStar()! Can you add a third star?",
code: "c.fillStyle = \"gold\";\nvar size = 100;\n\nfunction fillStar(x, y, s) {\n c.beginPath();\n c.moveTo(x, y);\n c.lineTo(x + s, y);\n c.lineTo(x + s * 0.15, y + s * 0.5);\n c.lineTo(x + s / 2, y - s * 0.4);\n c.lineTo(x + s * 0.85, y + s * 0.5);\n c.lineTo(x, y);\n c.fill();\n}\n\nfillStar(50, 100, size);\nfillStar(150, 150, size);",
lessonSection: "More Functions",
tutorImage: 5,
},
{ message: "Wow, four stars in a row using functions and for loops! See how this works? Can you make it eight stars?",
code: "c.fillStyle = \"gold\";\nvar size = 40;\n\nfunction fillStar(x, y, s) {\n c.beginPath();\n c.moveTo(x, y);\n c.lineTo(x + s, y);\n c.lineTo(x + s * 0.15, y + s * 0.5);\n c.lineTo(x + s / 2, y - s * 0.4);\n c.lineTo(x + s * 0.85, y + s * 0.5);\n c.lineTo(x, y);\n c.fill();\n}\n\nfor (var i = 0; i < 4; i = i + 1) {\n fillStar(i * size, 50, size);\n}\n",
},
{ message: "Two loops to make rows of stars! Try changing <span class=tutor-code>size</span> to make them bigger!",
code: "c.fillStyle = \"gold\";\nvar size = 40;\n\nfor (var i = 0; i < 5; i = i + 1) {\n for (var j = 0; j < 3; j = j + 1) {\n fillStar(i * size, j * size, size);\n }\n}\n\nfunction fillStar(x, y, s) {\n c.beginPath();\n c.moveTo(x, y + s * 0.4);\n c.lineTo(x + s, y + s * 0.4);\n c.lineTo(x + s * 0.15, y + s * 0.9);\n c.lineTo(x + s / 2, y);\n c.lineTo(x + s * 0.85, y + s * 0.9);\n c.lineTo(x, y + s * 0.4);\n c.fill();\n}\n",
lessonSection: "Nested Loops",
tutorImage: 6,
},
{ message: "Did you know you could do two loops like that, one inside the other?",
tutorImage: 4,
},
{ message: "Do you know how it works?",
},
{ message: "Look at the inner loop. <span class=tutor-code>j</span> will count 0, 1, 2. The first time <span class=tutor-code>j</span> counts 0, 1, 2, <span class=tutor-code>i</span> will be 0. The next time j counts 0, 1, 2, i will be 1.",
code: "c.fillStyle = \"gold\";\nvar size = 40;\n\nfor (var i = 0; i < 5; i = i + 1) {\n for (var j = 0; j < 3; j = j + 1) {\n fillStar(i * size, j * size, size);\n }\n}\n\nfunction fillStar(x, y, s) {\n c.beginPath();\n c.moveTo(x, y + s * 0.4);\n c.lineTo(x + s, y + s * 0.4);\n c.lineTo(x + s * 0.15, y + s * 0.9);\n c.lineTo(x + s / 2, y);\n c.lineTo(x + s * 0.85, y + s * 0.9);\n c.lineTo(x, y + s * 0.4);\n c.fill();\n}\n",
tutorImage: 1,
},
{ message: "So, for the first star, i = 0 and j = 0. On the second star, i = 0 and j = 1. Third, i = 0 and j = 2. Then, i = 1 and j = 0, i = 1 and j = 1, i = 1 and j = 2, and so on.",
},
{ message: "Can you figure out what order it draws the stars? Which star is drawn first? Which star is the second star drawn?",
tutorImage: 4,
},
{ message: "The first star will be the one at the top left. The second drawn is the one below it. Do you see why?",
tutorImage: 6,
},
{ message: "Wow! Full of stars! Try changing size!",
code: "c.fillStyle = \"gold\";\nvar size = 40;\nvar w = c.canvas.width;\nvar h = c.canvas.height;\nvar maxI = Math.floor(w / size);\nvar maxJ = Math.floor(h / size);\n\nfor (var i = 0; i < maxI; i = i + 1) {\n for (var j = 0; j < maxJ; j = j + 1) {\n fillStar(i * size, j * size, size);\n }\n}\n\nfunction fillStar(x, y, s) {\n c.beginPath();\n c.moveTo(x, y + s * 0.4);\n c.lineTo(x + s, y + s * 0.4);\n c.lineTo(x + s * 0.15, y + s * 0.9);\n c.lineTo(x + s / 2, y);\n c.lineTo(x + s * 0.85, y + s * 0.9);\n c.lineTo(x, y + s * 0.4);\n c.fill();\n}\n",
lessonSection: "Fun with Stars",
tutorImage: 5,
},
{ message: "How does this program fill the drawing area with stars? How does it know how many stars will fit?",
tutorImage: 4,
},
{ message: "<span class=tutor-code>c.canvas.width</span> is a way of getting the width of the drawing area. <span class=tutor-code>size</span> is a width of a star. So, <span class=tutor-code>c.canvas.width / size</span> is how many stars will fit across.",
tutorImage: 1,
},
{ message: "The code does something similar with the height to figure out how many stars fit up-and-down. The Math.floor() is just a way of rounding, it drops fractions off (so, for example. <span class=tutor-code>Math.floor(7.8)</span> is 7). Now do you see how it works?",
},
{ message: "Wow! Five stars, randomly placed, changing every time. Can you have it draw fifty stars? A hundred? How about bigger stars or smaller stars?",
code: "c.fillStyle = \"gold\";\nvar n = 5;\nvar size = 20;\nvar w = c.canvas.width;\nvar h = c.canvas.height;\n\nfor (var i = 0; i < n; i = i + 1) {\n var x = (w - size) * Math.random();\n var y = (h - size) * Math.random();\n fillStar(x, y, size);\n}\n\nfunction fillStar(x, y, s) {\n c.beginPath();\n c.moveTo(x, y + s * 0.4);\n c.lineTo(x + s, y + s * 0.4);\n c.lineTo(x + s * 0.15, y + s * 0.9);\n c.lineTo(x + s / 2, y);\n c.lineTo(x + s * 0.85, y + s * 0.9);\n c.lineTo(x, y + s * 0.4);\n c.fill();\n}\n",
tutorImage: 2,
},
{ message: "So, how does this work? <span class=tutor-code>n</span> is the number of stars. <span class=tutor-code>size</span> is the size of the stars. <span class=tutor-code>c.fillStyle</span> is the color of the stars. But how does it place them randomly?",
tutorImage: 4,
},
{ message: "<span class=tutor-code>Math.random()</span> gives us a random number between 0.0 and 1.0, a different one each time we use it.",
tutorImage: 1,
},
{ message: "See the line <span class=tutor-code>var x = (w - size) * Math.random()</span>? That figures out how far across to put the star. Can you figure out what exactly it is doing?",
},
{ message: "It takes the width of the drawing area (which is <span class=tutor-code>c.canvas.width</span> and stored in the variable called <span class=tutor-code>w</span>), subtracts off the <span class=tutor-code>size</span> (so the star will fit), and then multiplies by a random number between 0.0 and 1.0. So, if the random number is 0.5, the star will appear about halfway across.",
tutorImage: 3,
},
{ message: "The code does something similar with <span class=tutor-code>c.canvas.height</span> to determine where to put the star vertically. Does it make more sense now?",
},
// Draw many lines to point
{ message: "I'm tired of seeing stars. Let's go back to lines. Here are two separate lines. But see how much code is repeated? You know that isn't good. What can we do?",
code: "// First line\nc.beginPath();\nc.moveTo(20, 20);\nc.lineTo(200, 20);\nc.stroke();\n// Second line\nc.beginPath();\nc.moveTo(20, 50);\nc.lineTo(200, 50);\nc.stroke();\n",
lessonSection: "Even More Functions",
tutorImage: 1,
},
{ message: "Functions! We need a drawLine() function so we can draw lines more easily. Let me write that for you. Ta dah! Can you use this new function to draw one line from (20, 20) to (200, 20) and another from (20, 50) to (200, 50)?",
code: "function drawLine(x1, y1, x2, y2) {\n c.beginPath();\n c.moveTo(x1, y1);\n c.lineTo(x2, y2);\n c.stroke();\n}\n\n",
tutorImage: 6,
},
{ message: "Did you get it? Here is one version. Can you add a third line below the other two?",
code: "function drawLine(x1, y1, x2, y2) {\n c.beginPath();\n c.moveTo(x1, y1);\n c.lineTo(x2, y2);\n c.stroke();\n}\n\ndrawLine(20, 20, 200, 20);\ndrawLine(20, 50, 200, 50);\n",
tutorImage: 3,
},
{ message: "Three vertical lines using for loop. Can you make it ten lines?",
code: "function drawLine(x1, y1, x2, y2) {\n c.beginPath();\n c.moveTo(x1, y1);\n c.lineTo(x2, y2);\n c.stroke();\n}\n\nfor (var i = 0; i < 3; i = i + 1) {\n var x = 20 + i * 30;\n drawLine(x, 20, x, 200);\n}\n",
lessonSection: "Fun with Lines",
tutorImage: 1,
},
{ message: "Ten lines coming to a point. That's kind of cool, isn't it? Try moving the point!",
code: "function drawLine(x1, y1, x2, y2) {\n c.beginPath();\n c.moveTo(x1, y1);\n c.lineTo(x2, y2);\n c.stroke();\n}\n\nvar px = 200;\nvar py = 150;\nfor (var i = 0; i < 10; i = i + 1) {\n var y = i * 30;\n drawLine(0, y, px, py);\n}\n",
tutorImage: 2,
},
{ message: "Ready for something harder? Now the lines fan to the right. Can you also add in the ones we saw last, going to the left?",
code: "function drawLine(x1, y1, x2, y2) {\n c.beginPath();\n c.moveTo(x1, y1);\n c.lineTo(x2, y2);\n c.stroke();\n}\n\nvar px = 200;\nvar py = 150;\nfor (var i = 0; i < 10; i = i + 1) {\n var y = i * 30;\n drawLine(400, y, px, py);\n}\n",
tutorImage: 3,
},
{ message: "Did you get it? There should be a second drawLine() and starting from the left means x1 in drawLine() should be 0.",
tutorImage: 1,
},
{ message: "This is one way to do it. Try moving the point now! Fun, isn't it?",
code: "function drawLine(x1, y1, x2, y2) {\n c.beginPath();\n c.moveTo(x1, y1);\n c.lineTo(x2, y2);\n c.stroke();\n}\n\nvar px = 200;\nvar py = 150;\nfor (var i = 0; i < 10; i = i + 1) {\n var y = i * 30;\n drawLine(0, y, px, py);\n drawLine(400, y, px, py);\n}\n",
tutorImage: 2,
},
{ message: "Hey, look at this! Isn't that cool? It uses the drawing area width and height now to fit in as many lines as it can! Try changing num, px, or py!",
code: "var num = 20;\nvar px = 200;\nvar py = 150;\nvar w = c.canvas.width;\nvar h = c.canvas.height;\n\nfor (var i = 0; i < num; i = i + 1) {\n var y = i * h / (num - 1);\n drawLine(w, y, px, py);\n var x = y * w / h;\n drawLine(x, h, px, py);\n}\n\nfunction drawLine(x1, y1, x2, y2) {\n c.beginPath();\n c.moveTo(x1, y1);\n c.lineTo(x2, y2);\n c.stroke();\n}\n",
tutorImage: 5,
},
{ message: "See how it works? The first drawLine() draws lines from (w, y) to (px, py). <span class=tutor-code>w</span> is always the same and is the width of the drawing area. <span class=tutor-code>y</span> starts at 0 and increases until it is the height of the drawing area. So, it draws all those lines that go to the right side.",
tutorImage: 1,
},
{ message: "The second drawLine() does something similar for all the lines going to the bottom, drawing from (x, h) to (px, py).",
},
{ message: "Can you make lines from all sides? You will need four drawLine() and your two new ones will use 0 instead of w and h. Can you do it?",
code: "var num = 20;\nvar px = 200;\nvar py = 150;\nvar w = c.canvas.width;\nvar h = c.canvas.height;\n\nfor (var i = 0; i < num; i = i + 1) {\n var y = i * h / (num - 1);\n drawLine(w, y, px, py);\n var x = y * w / h;\n drawLine(x, h, px, py);\n}\n\nfunction drawLine(x1, y1, x2, y2) {\n c.beginPath();\n c.moveTo(x1, y1);\n c.lineTo(x2, y2);\n c.stroke();\n}\n",
tutorImage: 4,
},
{ message: "I like mine blue. Play with it!",
code: "var num = 10;\nvar px = 200;\nvar py = 150;\nvar w = c.canvas.width;\nvar h = c.canvas.height;\n\nc.strokeStyle = \"blue\";\nfor (var i = 0; i < num; i = i + 1) {\n var y = i * h / (num - 1);\n drawLine(0, y, px, py);\n drawLine(w, y, px, py);\n var x = y * w / h;\n drawLine(x, 0, px, py);\n drawLine(x, h, px, py);\n}\n\nfunction drawLine(x1, y1, x2, y2) {\n c.beginPath();\n c.moveTo(x1, y1);\n c.lineTo(x2, y2);\n c.stroke();\n}\n",
tutorImage: 2,
},
// Fan curves
{ message: "Sometimes, when I get bored in class, I draw these. Do you like them too? Can you make it so it is drawn with even more lines?",
code: "var num = 10;\nvar w = c.canvas.width;\nvar h = c.canvas.height;\n\nfor (var i = 0; i < num; i = i + 1) {\n var x = i * w / (num - 1);\n var y = i * h / (num - 1);\n drawLine(0, y, x, h);\n}\n\nfunction drawLine(x1, y1, x2, y2) {\n c.beginPath();\n c.moveTo(x1, y1);\n c.lineTo(x2, y2);\n c.stroke();\n}\n",
lessonSection: "More Fun with Lines",
tutorImage: 7,
},
{ message: "Can you add another curve at top right? You will need another drawLine() but use x, 0, w, y.",
code: "var num = 10;\nvar w = c.canvas.width;\nvar h = c.canvas.height;\n\nfor (var i = 0; i < num; i = i + 1) {\n var x = i * w / (num - 1);\n var y = i * h / (num - 1);\n drawLine(0, y, x, h);\n}\n\nfunction drawLine(x1, y1, x2, y2) {\n c.beginPath();\n c.moveTo(x1, y1);\n c.lineTo(x2, y2);\n c.stroke();\n}\n",
tutorImage: 1,
},
{ message: "Pretty! Try other colors and changing the number of lines!",
code: "var num = 30;\nvar w = c.canvas.width;\nvar h = c.canvas.height;\n\nc.strokeStyle = \"gold\";\nfor (var i = 0; i < num; i = i + 1) {\n var x = i * w / (num - 1);\n var y = i * h / (num - 1);\n drawLine(0, y, x, h);\n drawLine(x, 0, w, y);\n}\n\nfunction drawLine(x1, y1, x2, y2) {\n c.beginPath();\n c.moveTo(x1, y1);\n c.lineTo(x2, y2);\n c.stroke();\n}\n",
tutorImage: 5,
},
{ message: "Can you make it so the two curves are two different colors?",
tutorImage: 1,
},
{ message: "Here is one version. Try changing the colors!",
code: "var num = 30;\nvar w = c.canvas.width;\nvar h = c.canvas.height;\n\nfor (var i = 0; i < num; i = i + 1) {\n var x = i * w / (num - 1);\n var y = i * h / (num - 1);\n c.strokeStyle = \"gold\";\n drawLine(0, y, x, h);\n c.strokeStyle = \"red\";\n drawLine(x, 0, w, y);\n}\n\nfunction drawLine(x1, y1, x2, y2) {\n c.beginPath();\n c.moveTo(x1, y1);\n c.lineTo(x2, y2);\n c.stroke();\n}\n",
tutorImage: 2,
},
{ message: "Here is a crazy fun random colors changing version. It's complicated, but take a look. Can you guess how it works?",
code: "var num = 30;\nvar w = c.canvas.width;\nvar h = c.canvas.height;\n\nfor (var i = 0; i < num; i = i + 1) {\n var x = i * w / (num - 1);\n var y = i * h / (num - 1);\n c.strokeStyle = randomRGBA();\n drawLine(0, y, x, h);\n c.strokeStyle = randomRGBA();\n drawLine(x, 0, w, y);\n}\n\nfunction randomRGBA() {\n var r = randInt(255);\n var g = randInt(255);\n var b = randInt(255);\n var a = Math.random();\n var rgba = [r,g,b,a].join(\",\");\n return \"rgba(\" + rgba + \")\";\n}\nfunction randInt(limit) {\n var x = Math.random() * limit;\n return Math.floor(x);\n}\nfunction drawLine(x1, y1, x2, y2) {\n c.beginPath();\n c.moveTo(x1, y1);\n c.lineTo(x2, y2);\n c.stroke();\n}\n",
tutorImage: 5,
},
{ message: "Wow, it's great with thicker lines! Play with it some more! Try changing it so it draws the curves using more lines!",
code: "var num = 30;\nvar w = c.canvas.width;\nvar h = c.canvas.height;\n\nc.lineWidth = 5;\nfor (var i = 0; i < num; i = i + 1) {\n var x = i * w / (num - 1);\n var y = i * h / (num - 1);\n c.strokeStyle = randomRGBA();\n drawLine(0, y, x, h);\n c.strokeStyle = randomRGBA();\n drawLine(x, 0, w, y);\n}\n\nfunction randomRGBA() {\n var r = randInt(255);\n var g = randInt(255);\n var b = randInt(255);\n var a = Math.random();\n var rgba = [r,g,b,a].join(\",\");\n return \"rgba(\" + rgba + \")\";\n}\nfunction randInt(limit) {\n var x = Math.random() * limit;\n return Math.floor(x);\n}\nfunction drawLine(x1, y1, x2, y2) {\n c.beginPath();\n c.moveTo(x1, y1);\n c.lineTo(x2, y2);\n c.stroke();\n}\n",
},
// Quiz
{ message: "You know programming? Show me! Can make a function called fillSquare() and then use that to draw a second bigger square? Hint: You'll need something like <span class=tutor-code>function fillSquare(left, top, size) {</span>",
code: "c.fillRect(20, 30, 100, 100);\n",
lessonSection: "Quiz: Functions",
tutorImage: 3,
},
{ message: "Did you get it? Something like this? Now can you make the first square also use fillSquare() instead of calling fillRect()?",
code: "c.fillRect(20, 30, 100, 100);\n\nfillSquare(200, 70, 150);\nfunction fillSquare(x, y, s) {\n c.fillRect(x, y, s, s);\n}\n",
},
{ message: "I drew one line, but just one line is boring. Don't you want more? It would be easier to add more lines if we had that drawLine() function again. Can you write that and then make this line use it? Hint: Remember a function is declared like this, <span class=tutor-code>function doStuff(a, b, c, d) {</span>",
code:"c.beginPath();\nc.moveTo(30, 20);\nc.lineTo(200, 50);\nc.stroke();\n",
tutorImage: 1,
},
{ message: "Ahh, isn't that better? Now, can you add another line below this one?",
code: "drawLine(30, 20, 200, 50);\n\nfunction drawLine(x1, y1, x2, y2) {\n c.beginPath();\n c.moveTo(x1, y1);\n c.lineTo(x2, y2);\n c.stroke();\n}\n"
},
{ message: "Isn't it easier to add more lines after you created that function?",
tutorImage: 3,
},
{ message: "It's so easy to add more lines, I think we should add lots more! Give me ten lines total, one on top of the other, separated by 10! Hint: This is easier with a for loop. For loops look like <span class=tutor-code>for (var i = 0; i < 3; i += 1) {</span>",
tutorImage: 2,
},
{ message: "Did you get something like this? You're getting good! Try playing with the numbers! I like n of 40 and dy of 5!",
code: "var n = 10;\nvar dy = 10;\nfor (var i = 0; i < n; i = i + 1) {\n var y = 20 + i * dy;\n drawLine(30, y, 200, y + 30);\n}\n\nfunction drawLine(x1, y1, x2, y2) {\n c.beginPath();\n c.moveTo(x1, y1);\n c.lineTo(x2, y2);\n c.stroke();\n}\n",
tutorImage: 3,
},
// Two boxes and a white box
{ message: "Feels like forever since we saw a box, I'm starting to miss them. Hey, look, boxes can erase! Try moving the white box from (50, 50) to (20, 20)!",
code: "function fillSquare(w, h, s) {\n c.fillRect(w, h, s, s);\n}\n\nc.fillStyle = \"blue\";\nfillSquare(10, 10, 100);\nfillSquare(100, 100, 100);\nc.fillStyle = \"white\";\nfillSquare(50, 50, 100);\n",
lessonSection: "Erasing",
tutorImage: 1,
},
{ message: "Order matters. Last one to draw gets to stay! Try moving the white box now!",
code: "function fillSquare(w, h, s) {\n c.fillRect(w, h, s, s);\n}\n\nc.fillStyle = \"blue\";\nfillSquare(10, 10, 100);\nc.fillStyle = \"white\";\nfillSquare(50, 50, 100);\nc.fillStyle = \"blue\";\nfillSquare(100, 100, 100);\n",
tutorImage: 6,
},
// Make a little face using a box and clearRect()
{ message: "Another way to erase is with clearRect(). Can you make a little blue smiley face using these? It is almost done. You just need to move them.",
code: "c.fillStyle = \"blue\";\nc.fillRect(50, 20, 200, 200);\n// Eyes\nc.clearRect(80, 150, 20, 20);\nc.clearRect(120, 150, 20, 20);\n// Mouth\nc.clearRect(70, 50, 100, 10);\n",
tutorImage: 1,
},
{ message: "That's pretty funny, isn't it? It's cute!",
code: "c.fillStyle = \"blue\";\nc.fillRect(50, 20, 200, 200);\n// Eyes\nc.clearRect(80, 50, 20, 20);\nc.clearRect(150, 50, 20, 20);\n// Mouth\nc.clearRect(70, 150, 100, 10);\n",
tutorImage: 5,
},
// Clearing the screen
{ message: "A big clearRect() erases everything. Uncomment the big clearRect() (remove the <span class=tutor-code>//</span>) and see what happens.",
code: "var size = 80;\nvar offset = 20;\nvar w = c.canvas.width;\nvar h = c.canvas.height;\n\nc.fillStyle =\"rgba(0, 0, 255, 0.5)\";\nc.fillRect(offset, offset, size, size);\noffset = offset + 60;\nc.fillStyle =\"rgba(0, 255, 0, 0.5)\";\nc.fillRect(offset, offset, size, size);\n// c.clearRect(0, 0, w, h);\noffset = offset + 60;\nc.fillStyle =\"rgba(255, 0, 0, 0.5)\";\nc.fillRect(offset, offset, size, size);\n",
tutorImage: 1,
},
// Comments
{ message: "<span class=tutor-code>//</span> at the beginning of a line makes the line get ignored. That's called commenting out. Try adding <span class=tutor-code>//</span> before some of the other lines and see what happens!",
code: "var size = 80;\nvar offset = 20;\nvar w = c.canvas.width;\nvar h = c.canvas.height;\n\nc.fillStyle =\"rgba(0, 0, 255, 0.5)\";\nc.fillRect(offset, offset, size, size);\noffset = offset + 60;\nc.fillStyle =\"rgba(0, 255, 0, 0.5)\";\nc.fillRect(offset, offset, size, size);\n// c.clearRect(0, 0, w, h);\noffset = offset + 60;\nc.fillStyle =\"rgba(255, 0, 0, 0.5)\";\nc.fillRect(offset, offset, size, size);\n",
lessonSection: "Comments",
tutorImage: 7,
},
// Rotation and translation
{ message: "A purple box. But, wait, how did it get that far away from the edge? Try changing x and y.",
code: "var x = 50;\nvar y = 50;\n\nc.fillStyle = \"purple\";\nc.translate(x, y);\nc.fillRect(0, 0, 100, 100);\n",
lessonSection: "Rotation and Translation",
tutorImage: 1,
},
{ message: "The fillRect() says to make the (left, top) at (0, 0), so wouldn't you think the box would be at the upper left corner? But what is translate()?",
},
{ message: "translate() changes where (0, 0) is. After translate(50, 50), for example, anything that tries to draw at (0, 0) will draw at (50, 50). Drawing at (20, 20) would draw at (70, 70), since 50 + 20 = 70. See?",
tutorImage: 3,
},
{ message: "Why does this do what it does? It looks like it uses translate() twice. They seem to add to each other, right?",
code: "var x = 50;\nvar y = 50;\n\nc.fillStyle = \"purple\";\nc.translate(x, y);\nc.fillRect(0, 0, 100, 100);\nc.translate(x, y);\nc.fillRect(0, 0, 100, 100);\n",
tutorImage: 1,
},
{ message: "What will happen if you uncomment save() and restore()? Try it. Was it what you expected?",
code: "var x = 50;\nvar y = 50;\n\n// c.save();\nc.fillStyle = \"purple\";\nc.translate(x, y);\nc.fillRect(0, 0, 100, 100);\n// c.restore();\nc.translate(x, y);\nc.fillRect(0, 0, 100, 100);\n",
},
{ message: "The second box becomes black and gets drawn right on top of the first box! Why?",
code: "var x = 50;\nvar y = 50;\n\nc.save();\nc.fillStyle = \"purple\";\nc.translate(x, y);\nc.fillRect(0, 0, 100, 100);\nc.restore();\nc.translate(x, y);\nc.fillRect(0, 0, 100, 100);\n",
tutorImage: 4,
},
{ message: "restore() eliminates all the changes since the last save(). So, if you save, then change the fillStyle color or do translate(), then restore, everything goes back to what it was right before the save.",
tutorImage: 6,
},
{ message: "We can rotate stuff too. Try changing angle!",
code: "var x = 50;\nvar y = 50;\nvar angle = 0.1;\n\nc.translate(x, y);\nc.rotate(angle);\nc.strokeRect(0, 0, 100, 100);\n",
tutorImage: 3,
},
{ message: "You might be used to degrees? Angles in Javascript are in radians. 45 degrees is about 0.8 in radians. Try changing angle from 0.1 to 0.8!",
code: "var x = 50;\nvar y = 50;\nvar angle = 0.1;\n\nc.translate(x, y);\nc.rotate(angle);\nc.strokeRect(0, 0, 100, 100);\n",
tutorImage: 1,
},
{ message: "Spinning all the way around is 360 degrees, right? In radians, it is about 6.28.",
},
{ message: "Did you notice that this box is pivoting through its top left corner when you rotate it? That's the same spot we said to go with translate()."
},
{ message: "Try changing angle now. How is this different?",
code: "var x = 100;\nvar y = 100;\nvar angle = 0.1;\n\nc.translate(x, y);\nc.rotate(angle);\nc.strokeRect(-50, -50, 100, 100);\n",
tutorImage: 4,
},
{ message: "Kind of weird that the strokeRect() uses -50 for the left and top, isn't it? But the top left of the box is still at (50, 50), because we translated to (100, 100), and 100 - 50 = 50.",
tutorImage: 6,
},
{ message: "When we rotate, we rotate around (100, 100) because of the translate(). Oh, and (100, 100) is the center of the box! So, this box is rotating around its center!",
},
{ message: "translate() and rotate() add to any earlier translate() and rotate(), unless you restore() the old state. Try changing angle or uncommenting save() and restore()!",
code: "var x = 100;\nvar y = 100;\nvar angle = 0.1;\n\n// c.save();\nc.fillStyle = \"purple\";\nc.translate(x, y);\nc.rotate(angle);\nc.fillRect(-50, -50, 100, 100);\n// c.restore();\nc.translate(x, y);\nc.rotate(angle);\nc.fillRect(-50, -50, 100, 100);\n",
tutorImage: 1,
},
{ message: "Did that do what you expected? You see why it did what it did? With the save() and restore(), the second box is black and gets drawn right on top of the first purple box."
},
{ message: "Can you move the black box off the purple box? So you can see both?",
code: "var x = 100;\nvar y = 100;\nvar angle = 0.1;\n\nc.save();\nc.fillStyle = \"purple\";\nc.translate(x, y);\nc.rotate(angle);\nc.fillRect(-50, -50, 100, 100);\nc.restore();\nc.translate(x, y);\nc.rotate(angle);\nc.fillRect(-50, -50, 100, 100);\n",
tutorImage: 4,
},
{ message: "Here's one way. See how the translate() is to a different spot?",
code: "var x = 100;\nvar y = 100;\nvar angle = 0.1;\n\nc.save();\nc.fillStyle = \"purple\";\nc.translate(x, y);\nc.rotate(angle);\nc.fillRect(-50, -50, 100, 100);\nc.restore();\nc.translate(x * 2, y * 2);\nc.rotate(angle*2);\nc.fillRect(-50, -50, 100, 100);\n",
tutorImage: 3,
},
{ message: "Try changing angle now! Did you try a negative number of angle like -0.2?",
tutorImage: 1,
},
{ message: "Boxes rotated relative to their center are usually what you want. Look, I made you a function that makes boxes rotated to different angles! Can you use this new strokeSquare() function to add a few more boxes? Rotate your new boxes at any angle you want!",
code: "function strokeSquare(x, y, size, angle) {\n c.save();\n c.translate(x + size / 2, y + size / 2);\n c.rotate(angle);\n c.strokeRect(-size / 2, -size / 2,\n size, size);\n c.restore();\n}\n\nstrokeSquare(50, 50, 100, 0.2);\nstrokeSquare(180, 100, 50, -0.1);\n",
tutorImage: 5,
},
// Quiz
{ message: "Are you a coder now? Show me! Draw a box rotated by -0.5. Hint: Use c.rotate(); and c.strokeRect();",
code: "",
lessonSection: "Quiz: Rotation and Translation",
tutorImage: 2,
},
{ message: "Oh, is that too easy? You want harder problems? Draw five boxes on top of each other, each rotated by 0.3 more than the last!<br>Hint: c.translate() might be useful<br>Hint: For loops look like <span class=tutor-code>for (var i = 0; i < 5; i += 1) {</span>",
tutorImage: 6,
},
{ message: "Did you get something like this? See how this works? It first moves a spot with translate(), then repeatedly draws squares around that spot and rotates around that spot.",
code: "var angle = 0.2;\nvar num = 5;\nc.translate(150, 150);\nfor (var i = 0; i < num; i = i + 1) {\n c.strokeRect(-75, -75, 150, 150);\n c.rotate(angle);\n}\n",
tutorImage: 1,
},
{ message: "Play with it a bit! Try changing num and angle!",
tutorImage: 3,
},
// Draw a branching structure
{ message: "Wow, look at how much you've done! You're ready for a big fun challenge!",
code: "",
lessonSection: "A Hard Problem",
tutorImage: 1,
},
{ message: "Let's say we want to try to draw a tree. How might we do that?",
},
{ message: "Trees have branches. Here is a start. Can you add more branches?",
code: "drawLine(200, 300, 200, 200);\ndrawLine(200, 200, 160, 130);\ndrawLine(200, 200, 240, 130);\n\nfunction drawLine(x1, y1, x2, y2) {\n c.beginPath();\n c.moveTo(x1, y1);\n c.lineTo(x2, y2);\n c.stroke();\n}\n",
},
{ message: "That's a lot of work. That's not fun. There's got to be an easier way.",
tutorImage: 4,
},
{ message: "I'll try using variables. Does that make it easier? I'm not sure it does. What do you think?",
code: "var w = c.canvas.width;\nvar h = c.canvas.height;\n\ndrawLine(w * 0.5, h, w * 0.5, h * 0.7);\ndrawLine(w * 0.5, h * 0.7, w * 0.4, h * 0.5);\ndrawLine(w * 0.5, h * 0.7, w * 0.6, h * 0.5);\n\nfunction drawLine(x1, y1, x2, y2) {\n c.beginPath();\n c.moveTo(x1, y1);\n c.lineTo(x2, y2);\n c.stroke();\n}\n",
tutorImage: 6,
},
{ message: "That's still too much work. A for loop maybe? How might that work? I don't know. How would we draw two branches from every branch? I'm not sure. What do you think?",
},
{ message: "No, I can't see a way to draw trees easily using a for loop. What else do we have? Can we use functions?",
tutorImage: 1,
},
{ message: "Maybe a function could draw a branch. But how could every branch draw two more branches?",
},
{ message: "Let's start with a function that draws a branch. Here's one. It takes four numbers, <span class=tutor-code>x</span> and <span class=tutor-code>y</span> which say where to start, the length <span class=tutor-code>l</span>, and the <span class=tutor-code>direction</span> which says whether to draw left, right, or straight.",
code: "var w = c.canvas.width;\nvar h = c.canvas.height;\n\ndrawBranch(w / 2, h, 100, 0);\n\nfunction drawBranch(x, y, l, direction) {\n var x2 = x + l * direction;\n var y2 = y - l;\n drawLine(x, y, x2, y2);\n}\n\nfunction drawLine(x1, y1, x2, y2) {\n c.beginPath();\n c.moveTo(x1, y1);\n c.lineTo(x2, y2);\n c.stroke();\n}\n",
tutorImage: 4,
},
{ message: "<span class=tutor-code>direction</span> works by slanting the branch left (for negative numbers) or right (for positive). See? Try changing it!",
code: "var w = c.canvas.width;\nvar h = c.canvas.height;\n\ndrawBranch(w / 2, h, 100, -0.1);\n\nfunction drawBranch(x, y, l, direction) {\n var x2 = x + l * direction;\n var y2 = y - l;\n drawLine(x, y, x2, y2);\n}\n\nfunction drawLine(x1, y1, x2, y2) {\n c.beginPath();\n c.moveTo(x1, y1);\n c.lineTo(x2, y2);\n c.stroke();\n}\n"
},
{ message: "How can we make this draw two more branches now?",
tutorImage: 1,
},
{ message: "Ah, I know, make the function use itself! Can you see what this is doing?",
code: "var w = c.canvas.width;\nvar h = c.canvas.height;\n\ndrawBranch(w/2, h, 100, 0);\n\nfunction drawBranch(x, y, l, direction) {\n var x2 = x + l * direction;\n var y2 = y - l;\n drawLine(x, y, x2, y2);\n if (l > 20) {\n // Call drawBranch twice more\n drawBranch(x2, y2, l / 2, -0.8);\n drawBranch(x2, y2, l / 2, 0.8);\n }\n}\n\nfunction drawLine(x1, y1, x2, y2) {\n c.beginPath();\n c.moveTo(x1, y1);\n c.lineTo(x2, y2);\n c.stroke();\n}\n",
lessonSection: "Recursion",
tutorImage: 5,
},
{ message: "Functions that call themselves, that's crazy! Here's a version with some more variables to control it. That makes it easier to twiddle. Try playing with it! Change <span class=tutor-code>xScale</span>, <span class=tutor-code>yScale</span>, or <span class=tutor-code>minLength</span>! Cool, huh?",
code: "var minLength = 20;\nvar yScale = 0.5;\nvar xScale = 0.8;\nvar w = c.canvas.width;\nvar h = c.canvas.height;\n\ndrawBranch(w/2, h, 100, 0);\n\nfunction drawBranch(x, y, l, direction) {\n var x2 = x + l * direction;\n var y2 = y - l;\n drawLine(x, y, x2, y2);\n if (l > minLength) {\n // Call drawBranch twice more\n drawBranch(x2, y2, l * yScale, -xScale);\n drawBranch(x2, y2, l * yScale, xScale);\n }\n}\n\nfunction drawLine(x1, y1, x2, y2) {\n c.beginPath();\n c.moveTo(x1, y1);\n c.lineTo(x2, y2);\n c.stroke();\n}\n",
tutorImage: 3,
},
{ message: "There are a couple new things here. I'll stop to explain.",
tutorImage: 4,
},
{ message: "drawBranch() is a <i>recursive</i> function. That means it calls itself.",
},
{ message: "drawBranch() only calls itself if <span class=tutor-code>l > minLength</span>. <span class=tutor-code>l</span> gets smaller and smaller every time drawBranch() calls itself, so drawBranch() stops calling itself eventually when the length of a branch gets small enough.",
tutorImage: 1,
},
// Recursion
{ message: "Recursive functions are neat, but tricky! Let's do a bit more. Change this to call <span class=tutor-code>nSquareRecursive(4);</span> instead of <span class=tutor-code>nSquare(4);</span>",
code: "nSquare(4);\n\nfunction nSquare(n) {\n c.strokeStyle = \"blue\";\n for (var i = n; i > 0; i = i - 1) {\n c.strokeRect(i * 30, i * 30, 100, 100);\n }\n}\nfunction nSquareRecursive(n) {\n if (n > 1) {\n nSquareRecursive(n - 1);\n }\n c.strokeRect(n * 30, n * 30, 100, 100);\n}\n",
tutorImage: 6,
},
{ message: "Other than not being blue, it's the same, right? Do you see how they both work?",
},
{ message: "nSquare() uses a for loop, starts at 4, and counts down: 4, 3, 2, 1.",
tutorImage: 1,
},
{ message: "nSquareRecursive() calls itself with one less each time. So, when called with 4, it calls itself with 3, and that call calls itself with 2, and that call calls itself with 1. At one, it says, stop calling yourself, so it stops. And, on each call, it draws a box located at (n * 30, n * 30). See?",
},
{ message: "This little piece of code draws a shrinking pile of boxes. See how it works?",
code: "branchBox(100);\n\nfunction branchBox(size) {\n c.translate(size, size);\n c.strokeRect(-size, -size, size, size);\n if (size > 5) {\n branchBox(size * 0.65);\n }\n}\n",
tutorImage: 4,
},
{ message: "It does a translate() to the bottom right corner of the box, then draws the box back up behind it, so a box size big starting at (-size, -size). Then, as long as the boxes haven't gotten too small, it does that again by calling itself, but shrinking the size of the box the next time.",
tutorImage: 1,
},
{ message: "Here is the same thing using a for loop instead. Compare the two. See how they both work?",
code: "branchBoxForLoop(100);\n\nfunction branchBoxForLoop(size) {\n c.strokeStyle = \"blue\";\n for (var i = size; i > 4; i = i * 0.65) {\n c.translate(i, i);\n c.strokeRect(-i, -i, i, i);\n }\n}\nfunction branchBox(size) {\n c.translate(size, size);\n c.strokeRect(-size, -size, size, size);\n if (size > 5) {\n branchBox(size * 0.65);\n }\n}\n",
tutorImage: 4,
},
{ message: "The for loop starts by drawing a box of size 100, then one of 65, and so on. So does the recursive version, but the recursive version does it by calling itself.",
tutorImage: 1,
},
{ message: "You can change them if you want! Experiment!",
tutorImage: 5,
},
// Rotation and branching
{ message: "Back to trees. Wow, look! An even better tree!",
code: "var angle = 0.35;\nvar branchScale = 0.75;\nvar l = 75;\nvar minL = 5;\nvar w = c.canvas.width;\nvar h = c.canvas.height;\n\nc.translate(w/2, h);\ndrawBranch(l, 0);\n\nfunction drawBranch(l, direction) {\n c.save();\n c.rotate(angle * direction);\n c.fillRect(-l / 20, 0, l / 10, -l);\n if (l > minL) {\n c.translate(0, -l);\n drawBranch(l * branchScale, -1);\n drawBranch(l * branchScale, 1);\n }\n c.restore();\n}\n",
lessonSection: "Rotation, Translation, and Recursion",
tutorImage: 2,
},
{ message: "See how this works? It starts by rotating in the direction it is supposed to (<span class=tutor-code>direction</span> is -1 for left, 1 for right). If the branches are not too short yet, it translates to the end of a branch, then draws two more shorter branches, one to the left, one to the right.",
tutorImage: 6,
},
{ message: "The save() and restore() make it so that, when the program finishes drawing a left branch, the restore() removes the rotate and translate, getting us back to the right spot to do a right branch.",
tutorImage: 1,
},
{ message: "Try changing <span class=tutor-code>angle</span>, <span class=tutor-code>branchScale</span>, <span class=tutor-code>l</span>, and <span class=tutor-code>minL</span>! If you experiment a bit, you can get some really cool drawings. Play with it!",
tutorImage: 5,
},
{ message: "I think it would look even more like a tree if <span class=tutor-code>angle</span> was a little more random. Can you make angle bigger and then make rotate() use a number between 0 and angle? (Hint: <span class=tutor-code>Math.random()</span> makes a number from 0.0 to 1.0)",
tutorImage: 1,
},
{ message: "Here is one version. That's not too bad. You know what might make it even better? Can you make the <span class=tutor-code>branchScale</span> more random too?",
code: "var angle = 0.5;\nvar branchScale = 0.75;\nvar l = 75;\nvar minL = 5;\nvar w = c.canvas.width;\nvar h = c.canvas.height;\n\nc.translate(w/2, h);\ndrawBranch(l, 0);\n\nfunction drawBranch(l, direction) {\n c.save();\n // Make a number between 0 and angle\n var a = angle * Math.random();\n c.rotate(a * direction);\n c.fillRect(-l / 20, 0, l / 10, -l);\n if (l > minL) {\n c.translate(0, -l);\n drawBranch(l * branchScale, -1);\n drawBranch(l * branchScale, 1);\n }\n c.restore();\n}\n",
tutorImage: 3,
},
{ message: "Oh wow! Some of these look a lot like trees, don't they?",
code: "var angle = 0.5;\nvar branchScale = 0.75;\nvar l = 60;\nvar minL = 5;\nvar w = c.canvas.width;\nvar h = c.canvas.height;\n\nc.translate(w/2, h);\ndrawBranch(l, 0);\n\nfunction drawBranch(l, direction) {\n c.save();\n var a = angle * Math.random();\n c.rotate(a * direction);\n c.fillRect(-l / 20, 0, l / 10, -l);\n if (l > minL) {\n c.translate(0, -l);\n // Make a number between -0.1 and 0.1\n var b = 0.2 * (Math.random() - 0.5);\n // Now make it between 0.9 and 1.1\n b = b + 1;\n // And use it to change branchScale\n drawBranch(l * b * branchScale, -1);\n b = 1 + 0.2 * (Math.random() - 0.5);\n drawBranch(l * b * branchScale, 1);\n }\n c.restore();\n}\n",
tutorImage: 5,
},
{ message: "Try changing <span class=tutor-code>angle</span>, <span class=tutor-code>branchScale</span>, <span class=tutor-code>l</span>, and <span class=tutor-code>minL</span>! If you experiment a bit, you can get some pretty wild stuff!",
tutorImage: 2,
},
// Quiz on recursion, draw your own trees
{ message: "I bet you can draw your own trees now! Want to try? I'll help you!",
code: "",
lessonSection: "Drawing Your Own Tree",
tutorImage: 4,
},
{ message: "Let's draw different trees than we did before. Let's make this plant alternate between splitting off branches right and left. Some plants do that. Ready to start? I'll guide you through it!",
tutorImage: 1,
},
{ message: "So, first we need a branch. Let's make a function drawBranch() that takes two values, a length called <span class=tutor-code>l</span> and a <span class=tutor-code>direction</span>.",
code: "",
},
{ message: "Do you remember how to write a function like that? You don't need to make it do anything yet, just declare a function called drawBranch().",
tutorImage: 4,
},
{ message: "Were you thinking something like this?",
code: "function drawBranch(l, direction) {\n \n}\n",
tutorImage: 3,
},
{ message: "I've added some comments about what to do next. We need to start drawing our tree. Can you write the code beneath each of the comments?",
code: "function drawBranch(l, direction) {\n \n}\n\n// Use c.translate() to move to (200, 250).\n\n// Call drawBranch() with 50 as the length\n// and 1 as the direction\n",
tutorImage: 1,
},
{ message: "Okay, so you translate() so you start at the right spot and then you try to draw a branch. Now we need to make drawBranch() do something! Can you do what the comments say to do? You should get a single branch (which will look like the trunk of the tree) when you are done.",
code: "function drawBranch(l, direction) {\n // c.fillRect(left, top, width, height);\n // will draw a rectangle.\n // Use it do draw a rectangle that\n // starts at (-1, 0) and is 2 wide\n // and -l (that's l as in length) tall.\n \n}\n\nc.translate(200, 250);\ndrawBranch(50, 1);\n",
},
{ message: "You should have something like this now. Let's keep going! Look for the comments to tell you what to do next!",
code: "function drawBranch(l, direction) {\n c.fillRect(-1, 0, 2, -l);\n \n // Use c.translate() to move to the end\n // of the branch.\n // Hint: The end is at (0, -l), that's l\n // as in length.\n \n}\n\nc.translate(200, 250);\ndrawBranch(50, 1);\n",
tutorImage: 3,
},
{ message: "Great! Now you have drawn your first branch, moved to the end of that branch, and you're ready to draw more branches!",
code: "function drawBranch(l, direction) {\n c.fillRect(-1, 0, 2, -l);\n \n c.translate(0, -l);\n \n}\n\nc.translate(200, 250);\ndrawBranch(50, 1);\n",
},
{ message: "Let's stop for a second and think about what we're going to want to do next.",
tutorImage: 4,
},
{ message: "This plant is going to split off two branches, one going up, one going off to the side. We want the ones going off to the side to alternative, first right, then left, then right, then left.",
tutorImage: 1,
},
{ message: "We'll keep splitting and drawing and splitting and drawing until the branches get very short.",
},
{ message: "Let's add that next. We should only draw more branches if the length <span class=tutor-code>l</span> is more than 5. Can you add that?",
code: "function drawBranch(l, direction) {\n c.fillRect(-1, 0, 2, -l);\n \n c.translate(0, -l);\n \n // Replace true below with something that\n // checks if l is big enough\n if ( true ) {\n // Don't do anything here yet.\n // We'll add more branches later.\n }\n \n}\n\nc.translate(200, 250);\ndrawBranch(50, 1);\n",
tutorImage: 3,
},
{ message: "You should have this now.",
code: "function drawBranch(l, direction) {\n c.fillRect(-1, 0, 2, -l);\n \n c.translate(0, -l);\n \n if ( l > 5 ) {\n // Don't do anything here yet.\n // We'll add more branches later.\n }\n \n}\n\nc.translate(200, 250);\ndrawBranch(50, 1);\n",
tutorImage: 1,
},
{ message: "Let's add just the branch going straight up. Let's make it a little shorter that the last branch we drew. Follow the instructions in the comments.",
code: "function drawBranch(l, direction) {\n c.fillRect(-1, 0, 2, -l);\n \n c.translate(0, -l);\n \n if ( l > 5 ) {\n // Call drawBranch() with l * 0.8 and direction\n \n }\n \n}\n\nc.translate(200, 250);\ndrawBranch(50, 1);\n",
},
{ message: "Do you have this now? See what it does? It repeatedly draws a branch straight up, making the branch a little shorter each time. It looks like a line, but it's really a bunch of branches piled on top of each other.",
code: "function drawBranch(l, direction) {\n c.fillRect(-1, 0, 2, -l);\n \n c.translate(0, -l);\n \n if ( l > 5 ) {\n drawBranch(l * 0.8, direction);\n }\n \n}\n\nc.translate(200, 250);\ndrawBranch(50, 1);\n",
tutorImage: 3,
},
{ message: "Pretty boring so far?",
tutorImage: 6,
},
{ message: "It's about to get a lot more exciting!",
tutorImage: 5,
},
{ message: "Let's add the second branch! For that branch, we are going to rotate to the side indicated by direction, then draw a shorter branch. Follow the instructions in the comments.",
code: "function drawBranch(l, direction) {\n c.fillRect(-1, 0, 2, -l);\n \n c.translate(0, -l);\n \n if ( l > 5 ) {\n drawBranch(l * 0.8, direction);\n \n // Use c.rotate() with an angle of\n // 0.5 * direction\n \n \n // Call drawBranch with l * 0.7 and direction\n \n }\n \n}\n\nc.translate(200, 250);\ndrawBranch(50, 1);\n",
tutorImage: 1,
},
{ message: "Oh no! What did you do?",
tutorImage: 7,
},
{ message: "Just kidding! It's all fine!",
tutorImage: 2,
},
{ message: "This is just part of what is called debugging, getting the bugs (the problems) out.",
tutorImage: 4,
},
{ message: "So, that didn't do what you expected, did it?",
},
{ message: "Any idea what's wrong?",
tutorImage: 1,
},
{ message: "What's happening is that all those translate() and rotate() calls are piled on top of each other. We didn't really want that. When it's time to draw the second branch, we really want everything to be the way it was when we did the first branch, but it's not, we've translated and rotated all over the place.",
tutorImage: 4,
},
{ message: "Save and restore to the rescue! Can you add <span class=tutor-code>c.save();</span> and <span class=tutor-code>c.restore();</span> where the comments say to add them?",
code: "function drawBranch(l, direction) {\n // Save here!\n \n \n c.fillRect(-1, 0, 2, -l);\n \n c.translate(0, -l);\n \n if ( l > 5 ) {\n drawBranch(l * 0.8, direction);\n \n c.rotate(0.5 * direction);\n drawBranch(l * 0.7, direction);\n }\n \n // Restore here!\n \n}\n\nc.translate(200, 250);\ndrawBranch(50, 1);\n",
tutorImage: 1,
},
{ message: "That's mostly better. But it's only drawing one side of the tree! Why is that?",
code: "function drawBranch(l, direction) {\n c.save();\n \n c.fillRect(-1, 0, 2, -l);\n \n c.translate(0, -l);\n \n if ( l > 5 ) {\n drawBranch(l * 0.8, direction);\n \n c.rotate(0.5 * direction);\n drawBranch(l * 0.7, direction);\n }\n \n c.restore();\n}\n\nc.translate(200, 250);\ndrawBranch(50, 1);\n",
tutorImage: 4,
},
{ message: "Figure it out?",
},
{ message: "It's because we didn't switch direction. Direction is always the same as what it was at the start, it is always 1. It should alternate, 1, -1, 1, -1 ...",
tutorImage: 6,
},
{ message: "Can you make it so both of the drawBranch() calls inside of drawBranch() use -direction instead of direction?",
tutorImage: 1,
},
{ message: "Do you know have this? Wow, nice work!",