diff --git a/slides/index.css b/slides/index.css index 3b2ad88..0fbf1d6 100644 --- a/slides/index.css +++ b/slides/index.css @@ -2,13 +2,14 @@ html, body{ width:100%; height:100%; overflow: hidden; + cursor: none; } body{ margin:0; font-family: "FuturaHandwritten"; font-size: 22px; - cursor: none; + line-height: 1.5em; } /* SIMULATION and SLIDESHOW */ @@ -32,13 +33,16 @@ body{ -ms-user-select: none; user-select: none; } -#simulations canvas, #slideshow div{ +#simulations canvas, #slideshow > div{ position: absolute; } #slideshow .box.image{ background-size: 100% 100%; } -#slideshow .box.button{ +#slideshow .next_button{ + margin: 0 auto; + position: relative; + top: -10px; width: 300px; height: 20px; padding: 40px 0; @@ -46,7 +50,7 @@ body{ background-size: 100% auto; text-align: center; } -#slideshow .box.button:hover{ +#slideshow .next_button:hover{ background-position: 0 -100px; } diff --git a/slides/index.html b/slides/index.html index 6403fbb..1bf1839 100644 --- a/slides/index.html +++ b/slides/index.html @@ -43,6 +43,147 @@ Cursor is allowed to flow EVERYWHERE though... + + + + + + + +the +
+WISDOM and/or MADNESS +
+of CROWDS +
+ + +playing time: 30 min • by nicky case, april 2018 + + + +loading... + + + +let's play! → + + + +Why is it that the same people, +in different groups, can be kind, cruel, smart, stupid? +In this explorable explanation, +I'll show how the network of a group itself +can shape the people caught in its web. + + + + + + +blah blah blah blah blah
+let's make a network of friends! +
+ + +draw to connect + + + +scratch to   disconnect + + + +feel free to play around! when you're done, +let's continue → + + + +blah blah blah blah +blah blah blah blah +blah blah blah blah +blah blah blah blah +blah blah blah blah +thresholds NOT COUNTING THEMSELVES + + +# of drinker friends / # of total friends + + +% of friends are drinkers + + +(black line shows the 50% "majority" mark) + + +and next... + + + +blah blah drinking +blah blah next + + + +blah blah puzzle + + +HOW MANY PEEPS FOOLED: + + +blah blah puzzle +a winrar is you + + + +blah blah post-puzzle +simple contagion... + + + + + + + + + + + + + + + + +WIN + + +start sim! + + +next day >> + + +reset sim + + + + + + + + + + + + + + + + + + + + @@ -70,60 +211,3 @@ Cursor is allowed to flow EVERYWHERE though... - - - - - - -Why is it that the same people, -in different groups, can be kind, cruel, smart, stupid? -In this explorable explanation, -I'll show how the network of a group itself -can shape the people caught in its web. - - - -herp derp herp derp - - - - -blah blah blah blah blah
-let's make a network of friends! -
- -draw to connect - - -scratch to   disconnect - - -feel free to play around! when you're done,
-
- -let's continue → - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/slides/js/chapters/1_Networks.js b/slides/js/chapters/1_Networks.js index 7ea385f..80d998b 100644 --- a/slides/js/chapters/1_Networks.js +++ b/slides/js/chapters/1_Networks.js @@ -20,7 +20,11 @@ SLIDES.push( type:"sim", x:0, y:10, fullscreen: true, - network: {"contagion":0,"peeps":[[44,184,0],[155,215,0],[237,105,0],[309,213,0],[646,211,0],[328,305,0],[629,308,0],[417,111,0],[539,375,0],[216,299,0],[107,311,0],[-61,220,0],[87,452,0],[733,147,0],[760,293,0],[753,448,0],[744,46,0],[134,33,0],[929,181,0],[848,111,0],[1013,330,0],[880,269,0],[538,128,0],[208,391,0],[853,356,0]],"connections":[[5,6]]} + network: { + "contagion":0, + "peeps":[[44,184,0],[155,215,0],[237,105,0],[309,213,0],[646,211,0],[328,305,0],[629,308,0],[417,111,0],[539,375,0],[216,299,0],[107,311,0],[-61,220,0],[87,452,0],[733,147,0],[760,293,0],[753,448,0],[744,46,0],[134,33,0],[929,181,0],[848,111,0],[1013,330,0],[880,269,0],[538,128,0],[208,391,0],[853,356,0]], + "connections":[[5,6]] + } }, // "Connect" instruction (words & picture) @@ -52,18 +56,8 @@ SLIDES.push( type:"box", id:"end_words", text:"_1_tutorial_end", x:230, y:425, w:500, h:70, align:"center", - hidden:true - }, - { - type:"box", - id:"end_button", - button:"large", wiggle:true, - text:"_1_tutorial_next", x:330, y:440, - hidden:true, - onclick:function(){ - slideshow.next(); - } - }, + //hidden:true + } ], @@ -93,7 +87,7 @@ SLIDES.push( // If did both, show end if(state.canConnect && state.canDisconnect){ boxes.showChildByID("end_words"); - boxes.showChildByID("end_button"); + //boxes.showChildByID("end_button"); } // update # of connections in state @@ -104,20 +98,115 @@ SLIDES.push( }, // PLAY AROUND: how the "threshold" model workds +// diagonal { + + chapter: "Networks-Threshold", + clear:true, add:[ + + // TEXT + { + type:"box", + id:"_1_threshold", + text:"_1_threshold", x:80, y:25, w:300 + }, + { + type:"box", + id:"_1_threshold_end", + text:"_1_threshold_end", x:80, y:400, w:300 + }, + + // SIMULATION: THRESHOLD + { + type:"sim", + x:400, y:70, + fullscreen: true, + network: { + "contagion":0.5, + "peeps":[[95,65,0],[417,380,1],[52,340,0],[399,92,1]], + "connections":[[2,3],[3,1]], + }, + options:{ + infectedFrame: 2, + scale: 2 + } + } + ] }, -// PUZZLE: The "Majority Illusion" puzzle +// pre-puzzle ramble { - clear:true + remove:[ + { type:"box", id:"_1_threshold" }, + { type:"box", id:"_1_threshold_end" } + ], + add:[ + { + type:"box", + id:"_1_pre_puzzle", + text:"_1_pre_puzzle", x:80, y:25, w:325, h:540 + } + ] }, -// post-puzzle ramble + +// PUZZLE: The "Majority Illusion" puzzle { - clear:true + + chapter: "Networks-Majority", + + clear:true, + add:[ + + // The puzzle! + { + id:"puzzle", + type:"sim", + x:480-250, y:25, + fullscreen: true, + network: { + "contagion":0.5, + "peeps":[[106,106,1],[239,52,1],[376,110,1],[27,221,0],[54,365,0],[162,458,0],[308,467,0],[407,371,0],[453,241,0]], + "connections":[], + }, + options:{ + infectedFrame: 2, + scale: 1.5 + } + }, + + // Done? Let's go... (hidden at first...) + { + type:"box", + id:"_1_puzzle_end", + text:"_1_puzzle_end", x:680, y:430, w:300, align:"center" + //hidden:true + } + + ] + +}, + +// post-puzzle ramble, introduce simple contagion +{ + remove:[ + { type:"box", id:"_1_puzzle_end" } + ], + move:[ + // shift sim to side + {type:"sim", id:"puzzle", x:0} + ], + add:[ + // new text + { + type:"box", + id:"_1_post_puzzle", + text:"_1_post_puzzle", x:600, y:0, w:300 + } + ] } ); \ No newline at end of file diff --git a/slides/js/chapters/2_Simple_Contagion.js b/slides/js/chapters/2_Simple_Contagion.js index e69de29..e3a4819 100644 --- a/slides/js/chapters/2_Simple_Contagion.js +++ b/slides/js/chapters/2_Simple_Contagion.js @@ -0,0 +1,7 @@ +// 0 - INTRODUCTION +SLIDES.push( +{ + chapter: "Simple", + clear:true +} +); \ No newline at end of file diff --git a/slides/js/chapters/3_Complex_Contagion.js b/slides/js/chapters/3_Complex_Contagion.js index e69de29..229d359 100644 --- a/slides/js/chapters/3_Complex_Contagion.js +++ b/slides/js/chapters/3_Complex_Contagion.js @@ -0,0 +1,6 @@ +// 0 - INTRODUCTION +SLIDES.push( +{ + chapter: "Complex" +} +); \ No newline at end of file diff --git a/slides/js/chapters/4_Bonding_And_Bridging.js b/slides/js/chapters/4_Bonding_And_Bridging.js index e69de29..d02b14b 100644 --- a/slides/js/chapters/4_Bonding_And_Bridging.js +++ b/slides/js/chapters/4_Bonding_And_Bridging.js @@ -0,0 +1,6 @@ +// 0 - INTRODUCTION +SLIDES.push( +{ + chapter: "BB" +} +); \ No newline at end of file diff --git a/slides/js/chapters/5_Sandbox.js b/slides/js/chapters/5_Sandbox.js index e69de29..e046229 100644 --- a/slides/js/chapters/5_Sandbox.js +++ b/slides/js/chapters/5_Sandbox.js @@ -0,0 +1,6 @@ +// 0 - INTRODUCTION +SLIDES.push( +{ + chapter: "Sandbox" +} +); \ No newline at end of file diff --git a/slides/js/chapters/6_Conclusion.js b/slides/js/chapters/6_Conclusion.js index e69de29..02ac901 100644 --- a/slides/js/chapters/6_Conclusion.js +++ b/slides/js/chapters/6_Conclusion.js @@ -0,0 +1,6 @@ +// 0 - INTRODUCTION +SLIDES.push( +{ + chapter: "Conclusion" +} +); \ No newline at end of file diff --git a/slides/js/chapters/7_Credits.js b/slides/js/chapters/7_Credits.js index e69de29..00e4a1e 100644 --- a/slides/js/chapters/7_Credits.js +++ b/slides/js/chapters/7_Credits.js @@ -0,0 +1,6 @@ +// 0 - INTRODUCTION +SLIDES.push( +{ + chapter: "Credits" +} +); \ No newline at end of file diff --git a/slides/js/lib/helpers.js b/slides/js/lib/helpers.js index 840c5bd..ae2ee0a 100644 --- a/slides/js/lib/helpers.js +++ b/slides/js/lib/helpers.js @@ -45,4 +45,10 @@ function cloneObject(obj){ // Get words function getWords(wordsID){ return $("words#"+wordsID).innerHTML; -} \ No newline at end of file +} + +// Remove from array +function removeFromArray(array, item){ + var index = array.indexOf(item); + if(index>=0) array.splice(index,1); +} diff --git a/slides/js/sim/Connection.js b/slides/js/sim/Connection.js index 39e91ad..3094399 100644 --- a/slides/js/sim/Connection.js +++ b/slides/js/sim/Connection.js @@ -21,14 +21,9 @@ function Connection(config){ // Draw self.draw = function(ctx){ - /* - ctx.strokeStyle = "#444"; - ctx.lineWidth = self.uncuttable ? 6 : 3; // thick=uncuttable - ctx.beginPath(); - ctx.moveTo(self.from.x, self.from.y); - ctx.lineTo(self.to.x, self.to.y); - ctx.stroke(); - */ + + var s = self.sim.options.scale || 1; + ctx.save(); ctx.translate(self.from.x, self.from.y); var dx = self.to.x - self.from.x; @@ -37,9 +32,11 @@ function Connection(config){ var dist = Math.sqrt(dx*dx + dy*dy); self.sprite.scaleX = dist/300; self.sprite.scaleY = self.uncuttable ? 1 : 0.5; // thick=uncuttable + //self.sprite.scaleY *= s; self.sprite.rotation = a; self.sprite.draw(ctx); ctx.restore(); + }; // Hit Test with a LINE SEGMENT diff --git a/slides/js/sim/ConnectorCutter.js b/slides/js/sim/ConnectorCutter.js index 4f2ced4..08d7b34 100644 --- a/slides/js/sim/ConnectorCutter.js +++ b/slides/js/sim/ConnectorCutter.js @@ -95,14 +95,9 @@ function ConnectorCutter(config){ // Connecting! if(self.state==1){ - /*ctx.strokeStyle = "#ccc"; - ctx.lineWidth = 3; - ctx.beginPath(); - ctx.moveTo(self.connectFrom.x, self.connectFrom.y); - ctx.lineTo(self.connectTo.x, self.connectTo.y); - ctx.stroke();*/ var tempConnection = new Connection({ - from:self.connectFrom, to:self.connectTo + from:self.connectFrom, to:self.connectTo, + sim:self.sim }); ctx.save(); ctx.globalAlpha = 0.5; diff --git a/slides/js/sim/Peep.js b/slides/js/sim/Peep.js index 7e4642d..0cb52d4 100644 --- a/slides/js/sim/Peep.js +++ b/slides/js/sim/Peep.js @@ -1,8 +1,3 @@ -var PEEP_STATE_COLORS = { - 1: "#ccc", - 2: "#dd4040" -}; - function Peep(config){ var self = this; @@ -52,6 +47,21 @@ function Peep(config){ if(friend.infected) self.numInfectedFriends++; }); + // Past threshold? + self.isPastThreshold = false; + if(self.sim.contagion==0){ + // simple + if(self.numInfectedFriends>0) self.isPastThreshold = true; + }else{ + // complex + if(self.numFriends>0){ + var ratio = self.numInfectedFriends/self.numFriends; + if(ratio>=self.sim.contagion-0.0001){ // floating point errors + self.isPastThreshold = true; + } + } + } + }; // Body Sprite @@ -61,22 +71,50 @@ function Peep(config){ }); self.sprite.pivotX = 100; self.sprite.pivotY = 100; - self.sprite.scale = 0.3; + var _initSpriteScale = 0.3; + self.sprite.scale = _initSpriteScale; //self.sprite.gotoFrame(1); // Draw var radius = 25; - var barWidth = 30; + var barWidth = radius*1.75; var barHeight = 10; var bodyRotation = Math.TAU*Math.random(); + var PEEP_COLORS = [ + "#B4B4B4", // gray + "#F73C50", // red + "#FEE576", // yellow + "#86F5FB", // blue + "#7DE74E", // green + "#FBCBDC" // pink + ]; self.draw = function(ctx){ ctx.save(); ctx.translate(self.x, self.y); + var s; + if(s = self.sim.options.scale) ctx.scale(s,s); + // Circle + var infectedFrame = self.sim.options.infectedFrame || 1; + var infectedColor = PEEP_COLORS[infectedFrame]; + var myFrame = self.infected ? infectedFrame : 0; + var myColor = PEEP_COLORS[myFrame]; self.sprite.rotation = bodyRotation; - self.sprite.gotoFrame(self.infected ? 1 : 0); + if(self.isPastThreshold){ // highlight! + + ctx.globalAlpha = 0.4; + self.sprite.scale = _initSpriteScale*1.25; + + self.sprite.gotoFrame(infectedFrame); + self.sprite.draw(ctx); + + ctx.globalAlpha = 1; + self.sprite.scale = _initSpriteScale; + + } + self.sprite.gotoFrame(myFrame); self.sprite.draw(ctx); // Face @@ -91,63 +129,57 @@ function Peep(config){ // LABEL FOR INFECTED/FRIENDS, BAR, AND CONTAGION LEVEL // ////////////////////////////////////////////////////////// + // DON'T show bar if simple contagion if(self.sim.contagion>0){ ctx.save(); - // Say: Infected/Friends - ctx.translate(0,-42); - var labelNum = self.numInfectedFriends+"/"+self.numFriends; - var labelPercent = ""; - if(self.numFriends>0){ - labelPercent = Math.round(100*(self.numInfectedFriends/self.numFriends)) + "%"; - } - ctx.font = '12px sans-serif'; - ctx.fillStyle = myColor; - ctx.textAlign = "center"; + var bgColor = "#eee"; + var uiColor = "#666"; + + // Say: Infected/Friends (% then n/n) + ctx.translate(0,-43); + ctx.font = '10px FuturaHandwritten'; + ctx.fillStyle = uiColor; ctx.textBaseline = "middle"; ctx.fontWeight = "bold"; - ctx.fillText(labelNum, 0, 0); + ctx.textAlign = "center"; + if(self.numFriends>0){ + var labelNum = self.numInfectedFriends+"/"+self.numFriends; + var labelPercent = Math.round(100*(self.numInfectedFriends/self.numFriends)) + "%"; + var label = labelNum + "=" + labelPercent; + ctx.fillText(label, 0, 0); + }else{ + ctx.fillText("∅", 0, -1); + } - // A nice bar - ctx.translate(0,12); - ctx.lineWidth = 1; - - // the white fill - ctx.fillStyle = "#fff"; + // the gray bg + ctx.translate(0,10); + ctx.fillStyle = bgColor; ctx.beginPath(); ctx.rect(-barWidth/2, -barHeight/2, barWidth, barHeight); ctx.fill(); - // The color fills + // the color fill if(self.numFriends>0){ - ctx.fillStyle = PEEP_STATE_COLORS[2]; // state = 2 infected + ctx.fillStyle = infectedColor; ctx.beginPath(); ctx.rect(-barWidth/2, -barHeight/2, barWidth*(self.numInfectedFriends/self.numFriends), barHeight); ctx.fill(); } - // The outline - ctx.strokeStyle = myColor; - ctx.beginPath(); - if(self.numFriends>0){ - ctx.rect(-barWidth/2, -barHeight/2, barWidth, barHeight); - }else{ - ctx.rect(-barWidth/2, 0, barWidth, 0); - } - ctx.stroke(); - // a pointer for contagion level - ctx.translate(0, barHeight/2+2); - self._drawThreshold(ctx, self.sim.contagion); - - // Percent - ctx.font = '8px sans-serif'; - ctx.fillStyle = "rgba(0,0,0,0.8)"; - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.fontWeight = "bold"; - ctx.fillText(labelPercent, 0, -6); + ctx.translate(0, -barHeight/2); + ctx.save(); + ctx.translate(barWidth*self.sim.contagion - barWidth/2, 0); + ctx.lineCap = "butt"; + ctx.strokeStyle = uiColor; + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.moveTo(0,0); + ctx.lineTo(0,barHeight); + ctx.stroke(); + ctx.restore(); ctx.restore(); @@ -156,19 +188,6 @@ function Peep(config){ ctx.restore(); }; - self._drawThreshold = function(ctx, threshold){ - ctx.save(); - ctx.translate(barWidth*threshold - barWidth/2, 0); - - ctx.strokeStyle = "#000"; //PEEP_STATE_COLORS[2]; - ctx.lineWidth = 1; - ctx.beginPath(); - ctx.moveTo(0,0); - ctx.lineTo(0,-14); - ctx.stroke(); - - ctx.restore(); - } // Hit Test self.hitTest = function(x,y,buffer){ @@ -177,6 +196,8 @@ function Peep(config){ var dy = self.y-y; var dist2 = dx*dx+dy*dy; var r = radius+buffer; + var s; + if(s = self.sim.options.scale) r*=s; return (dist2