├── README.md ├── noise.png ├── favicon.ico ├── font ├── fontello.eot ├── fontello.ttf ├── fontello.woff └── fontello.svg ├── css ├── fontello-codes.css ├── fontello-ie7-codes.css ├── fontello-ie7.css ├── fontello.css └── fontello-embedded.css ├── package.json ├── LICENSE ├── server.js ├── index.html ├── textualizer.min.js ├── bg.js ├── client.js ├── main.css └── fss.min.js /README.md: -------------------------------------------------------------------------------- 1 | shareddeath 2 | =========== 3 | -------------------------------------------------------------------------------- /noise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhan-mstf/shareddeath/master/noise.png -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhan-mstf/shareddeath/master/favicon.ico -------------------------------------------------------------------------------- /font/fontello.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhan-mstf/shareddeath/master/font/fontello.eot -------------------------------------------------------------------------------- /font/fontello.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhan-mstf/shareddeath/master/font/fontello.ttf -------------------------------------------------------------------------------- /font/fontello.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhan-mstf/shareddeath/master/font/fontello.woff -------------------------------------------------------------------------------- /css/fontello-codes.css: -------------------------------------------------------------------------------- 1 | 2 | .icon-level-up:before { content: '\e801'; } /* '' */ 3 | .icon-cw-1:before { content: '\e802'; } /* '' */ 4 | .icon-language:before { content: '\e803'; } /* '' */ -------------------------------------------------------------------------------- /css/fontello-ie7-codes.css: -------------------------------------------------------------------------------- 1 | 2 | .icon-level-up { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 3 | .icon-cw-1 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 4 | .icon-language { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -------------------------------------------------------------------------------- /css/fontello-ie7.css: -------------------------------------------------------------------------------- 1 | [class^="icon-"], [class*=" icon-"] { 2 | font-family: 'fontello'; 3 | font-style: normal; 4 | font-weight: normal; 5 | 6 | /* fix buttons height */ 7 | line-height: 1em; 8 | 9 | /* you can be more comfortable with increased icons size */ 10 | /* font-size: 120%; */ 11 | } 12 | 13 | .icon-level-up { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 14 | .icon-cw-1 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 15 | .icon-language { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "death", 3 | "version": "1.0.4", 4 | "description": "Shared Death", 5 | "keywords": [ 6 | "Data Process", 7 | "Death" 8 | ], 9 | "author": { 10 | "name": "mustafa ilhan", 11 | "email": "mustafa@kulu.be", 12 | "url": "http://www.kulu.be/" 13 | }, 14 | "subdomain": "shareddeath", 15 | "domains": [ 16 | "shareddeath.kulu.be" 17 | ], 18 | "homepage": "http://shareddeath.kulu.be/", 19 | "engines": { 20 | "node": ">= 0.6.0", 21 | "npm": ">= 1.0.0" 22 | }, 23 | "private": true, 24 | "dependencies": { 25 | "express": ">= 3.2.4", 26 | "socket.io": ">= 0.9.14", 27 | "twit": ">= 1.1.6" 28 | }, 29 | "scripts": { 30 | "start": "server.js" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Mustafa İlhan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /font/fontello.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Copyright (C) 2012 by original authors @ fontello.com 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /css/fontello.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'fontello'; 3 | src: url('../font/fontello.eot?12562311'); 4 | src: url('../font/fontello.eot?12562311#iefix') format('embedded-opentype'), 5 | url('../font/fontello.woff?12562311') format('woff'), 6 | url('../font/fontello.ttf?12562311') format('truetype'), 7 | url('../font/fontello.svg?12562311#fontello') format('svg'); 8 | font-weight: normal; 9 | font-style: normal; 10 | } 11 | /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ 12 | /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ 13 | /* 14 | @media screen and (-webkit-min-device-pixel-ratio:0) { 15 | @font-face { 16 | font-family: 'fontello'; 17 | src: url('../font/fontello.svg?12562311#fontello') format('svg'); 18 | } 19 | } 20 | */ 21 | 22 | [class^="icon-"]:before, [class*=" icon-"]:before { 23 | font-family: "fontello"; 24 | font-style: normal; 25 | font-weight: normal; 26 | speak: none; 27 | 28 | display: inline-block; 29 | text-decoration: inherit; 30 | width: 1em; 31 | margin-right: .2em; 32 | text-align: center; 33 | /* opacity: .8; */ 34 | 35 | /* For safety - reset parent styles, that can break glyph codes*/ 36 | font-variant: normal; 37 | text-transform: none; 38 | 39 | /* fix buttons height, for twitter bootstrap */ 40 | line-height: 1em; 41 | 42 | /* Animation center compensation - margins should be symmetric */ 43 | /* remove if not needed */ 44 | margin-left: .2em; 45 | 46 | /* you can be more comfortable with increased icons size */ 47 | /* font-size: 120%; */ 48 | 49 | /* Uncomment for 3D effect */ 50 | /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ 51 | } 52 | 53 | .icon-level-up:before { content: '\e801'; } /* '' */ 54 | .icon-cw-1:before { content: '\e802'; } /* '' */ 55 | .icon-language:before { content: '\e803'; } /* '' */ -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | /*! 2 | The MIT License 3 | 4 | Copyright (c) 2013 Mustafa İlhan 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | var express = require('express') 26 | , app = express() 27 | , server = require('http').createServer(app) 28 | , io = require('socket.io').listen(server) 29 | , Twit = require('twit'); 30 | 31 | io.set('log level', 1); 32 | 33 | // New call to compress content 34 | app.use(express.compress()); 35 | 36 | // Routing 37 | app.use(express.static(__dirname), { maxAge: 86400000 }); 38 | 39 | // Start server 40 | server.listen(8080); 41 | 42 | // Twitter OAuth 43 | var T_en = new Twit({ 44 | consumer_key: '_your_consumer_key_' 45 | , consumer_secret: '_your_consumer_secret_' 46 | , access_token: '_your_access_token_' 47 | , access_token_secret: '_your_access_secret_' 48 | }); 49 | var T_tr = new Twit({ 50 | consumer_key: '_your_consumer_key_' 51 | , consumer_secret: '_your_consumer_secret_' 52 | , access_token: '_your_access_token_' 53 | , access_token_secret: '_your_access_secret_' 54 | }); 55 | 56 | // Filter twitter public stream by the word. 57 | var stream_en = T_en.stream('statuses/filter', { track: '#death,death,dying' }); 58 | var stream_tr = T_tr.stream('statuses/filter', { track: '#ölüm,ölüm,ölü,ölüyorum,öleceğim,öldüreceğim,geber,geberiyorum' }); 59 | 60 | // Start streaming 61 | stream_en.on('tweet', function (tweet) { 62 | // Filter tweets that contain link 63 | if (!tweet.text.match(/http/) && !tweet.text.match(/@/)) { 64 | // Send tweets to the client 65 | io.sockets.volatile.emit('tweet_en', { 66 | user: tweet.user.screen_name 67 | , text: tweet.text 68 | }); 69 | } 70 | }); 71 | 72 | stream_tr.on('tweet', function (tweet) { 73 | // Filter tweets that contain link 74 | if (!tweet.text.match(/http/)) { 75 | // Send tweets to the client 76 | io.sockets.volatile.emit('tweet_tr', { 77 | user: tweet.user.screen_name 78 | , text: tweet.text 79 | }); 80 | } 81 | }); 82 | 83 | var connectCounter = 0 84 | , streamStarted = true; 85 | 86 | io.sockets.on('connection', function (socket) { 87 | if (!connectCounter & !streamStarted) { 88 | stream_en.start(); 89 | stream_tr.start(); 90 | console.log("streams started."); 91 | } 92 | connectCounter++; 93 | 94 | socket.on('disconnect', function() { 95 | connectCounter--; 96 | if (!connectCounter) { 97 | stream_en.stop(); 98 | stream_tr.stop(); 99 | streamStarted = false; 100 | console.log("streams stoped."); 101 | } 102 | }); 103 | }); 104 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Shared Death 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 24 | 25 | 26 | 27 | 28 |
29 |
30 |
31 |
32 |
33 | 34 | 35 |
36 | 37 | 38 |
39 |
40 | 41 | 42 |
43 |
44 | 45 | 46 | English 47 | | 48 | Türkçe 49 | 50 |
51 |
52 | 53 | 54 |
55 | 56 | 69 | 70 |
71 | ABOUT 72 |
73 |

Shared Death is a web-based artwork which filters shared thoughts containing keywords like "death", "dying" from the Twitter in real time. Its purpose is to help people better understand how other people thinks and feels about the shared destiny of all living things in the planet. You can participate in this experiment by sending tweet with #death keyword. 74 | Read More 75 |
76 | Mustafa İlhan - @mustilica

77 |
78 |
79 | 80 | 81 |
82 | 85 |
86 | 87 | 88 |
89 |
90 |
91 | 92 | 93 |
94 |
95 |
96 | 97 |
98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /textualizer.min.js: -------------------------------------------------------------------------------- 1 | /*! https://github.com/krisk/textualizer */ 2 | (function(b,j){function x(a){var b,d,f,n={};if(j.getComputedStyle)if(a=j.getComputedStyle(a,null),a.length){d=0;for(f=a.length;d").text(i),l.append(r.domNode),h.push(r));var s=d.centered?(z-l.height())/2:0;b.each(h,function(a,b){b.pos=b.domNode.position();b.domNode=b.domNode.clone();b.pos.top+=s;b.domNode.css({left:b.pos.left,top:b.pos.top,position:"absolute"});g.characterList.push(b)});l.html("");o=g}if(v){var h=v,x=o,B=[],C=[],D=b.Deferred(),E,p;k=[function(a){p=b.Deferred();a.fadeOut(1E3, 6 | p.resolve);return p.promise()}];E=k[Math.floor(Math.random()*k.length)];var w;b.each(h.characterList,function(a,d){if(w=x.use(d.character))w.domNode=d.domNode,w.inserted=!0,B.push(w);else{var c=b.Deferred();C.push(c);E(d.domNode.delay(Math.random()*L)).done(function(){d.domNode.remove();c.resolve()})}});b.when.apply(null,C).done(function(){return D.resolve(B)});D.promise().done(function(a){var c=o,g=b.Deferred(),h=[];j.setTimeout(function(){b.each(a,function(a,c){var e=b.Deferred();c.domNode.animate({left:c.pos.left, 7 | top:c.pos.top},d.rearrangeDuration,e.resolve);h.push(e.promise())});b.when.apply(null,h).done(function(){j.setTimeout(function(){f(c).done(function(){g.resolve()})},J)})},I);g.promise().done(function(){e.resolve()})})}else f(o).done(function(){e.resolve()});v=o;e.promise().done(function(){a.trigger(M,{index:c});j.setTimeout(n,d.duration)})}}var G=this,t=[],u,e,v,s=null,i=!1,y=!1,z,F,m,h,l;"random"!==d.effect&&b.each(b.fn.textualizer.effects,function(){if(this[0]===d.effect)return s=this[1],!1});m= 8 | a.clone().removeAttr("id").appendTo(j.document.body);m.css(x(a[0]));m.css({position:"absolute",top:"-1000px"});l=b("
").css({position:"relative",visibility:"hidden"}).appendTo(m);a.css("overflow","hidden");h=b("
").css("position","relative").appendTo(a);z=a.height();F={bottom:z};this.data=function(a){this.stop();t=a;u=[]};this.stop=function(){this.pause();i=!1;v=null;e=0;h.empty();l.empty()};this.pause=function(){y=!0;i=!1};this.start=function(){0===t.length||i||(e=e||0,i=!0,y=!1,q(e))}; 9 | this.destroy=function(){h.parent().removeData("textualizer").end().remove();l.remove()};c&&c instanceof Array&&this.data(c)};b.fn.textualizer=function(){var a=arguments,c,d,f;f=this.data("textualizer");f||(c=[],1===a.length&&a[0]instanceof Array?c=a[0]:1===a.length&&"object"===typeof a[0]?d=a[0]:2===a.length&&(c=a[0],d=a[1]),0===c.length&&this.find("p").each(function(){c.push(b(this).text())}),this.html(""),f=new q(this,c,b.extend({},b.fn.textualizer.defaults,d)),this.data("textualizer",f));d=f;"string"=== 10 | typeof a[0]&&d[a[0]]&&d[a[0]].apply(d,Array.prototype.slice.call(a,1));return this};b.fn.textualizer.defaults={effect:"random",duration:2E3,rearrangeDuration:1E3,centered:!1,loop:!0};b.fn.textualizer.effects=[["none",function(a){a.container.append(a.item.domNode.show())}],["fadeIn",function(a){a.container.append(a.item.domNode.fadeIn(2E3,a.dfd.resolve));return a.dfd.promise()}],["slideLeft",function(a){a.item.domNode.appendTo(a.container).css({left:-1E3}).show().animate({left:a.item.pos.left},2E3, 11 | a.dfd.resolve);return a.dfd.promise()}],["slideTop",function(a){a.item.domNode.appendTo(a.container).css({top:-1E3}).show().animate({top:a.item.pos.top},2E3,a.dfd.resolve);return a.dfd.promise()}]]})(jQuery,window); -------------------------------------------------------------------------------- /bg.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) 2013 Mustafa İlhan released under the MIT license 3 | * based on https://github.com/wagerfield/flat-surface-shader 4 | */ 5 | 6 | (function(){ 7 | 8 | //------------------------------ 9 | // Mesh Properties 10 | //------------------------------ 11 | var MESH = { 12 | width: 1.2, 13 | height: 1.2, 14 | depth: 10, 15 | segments: 16, 16 | slices: 8, 17 | xRange: 0.8, 18 | yRange: 0.1, 19 | zRange: 1.0, 20 | ambient: '#555555', 21 | diffuse: '#FFFFFF', 22 | speed: 0.002 23 | }; 24 | 25 | //------------------------------ 26 | // Light Properties 27 | //------------------------------ 28 | var LIGHT = { 29 | count: 2, 30 | xyScalar: 1, 31 | zOffset: 100, 32 | ambient: '#0f0600', 33 | diffuse: '#1b1103', 34 | speed: 0.001, 35 | gravity: 1200, 36 | dampening: 0.95, 37 | minLimit: 10, 38 | maxLimit: null, 39 | minDistance: 20, 40 | maxDistance: 400, 41 | autopilot: false, 42 | draw: false, 43 | bounds: FSS.Vector3.create(), 44 | step: FSS.Vector3.create( 45 | Math.randomInRange(0.2, 1.0), 46 | Math.randomInRange(0.2, 1.0), 47 | Math.randomInRange(0.2, 1.0) 48 | ) 49 | }; 50 | 51 | //------------------------------ 52 | // Render Properties 53 | //------------------------------ 54 | var WEBGL = 'webgl'; 55 | var CANVAS = 'canvas'; 56 | var SVG = 'svg'; 57 | var RENDER = { 58 | renderer: CANVAS 59 | }; 60 | 61 | //------------------------------ 62 | // Export Properties 63 | //------------------------------ 64 | var EXPORT = { 65 | width: 2000, 66 | height: 1000, 67 | drawLights: false, 68 | minLightX: 0.4, 69 | maxLightX: 0.6, 70 | minLightY: 0.2, 71 | maxLightY: 0.4, 72 | export: function() { 73 | var l, x, y, light, 74 | depth = MESH.depth, 75 | zOffset = LIGHT.zOffset, 76 | autopilot = LIGHT.autopilot, 77 | scalar = this.width / renderer.width; 78 | 79 | LIGHT.autopilot = true; 80 | LIGHT.draw = this.drawLights; 81 | LIGHT.zOffset *= scalar; 82 | MESH.depth *= scalar; 83 | 84 | resize(this.width, this.height); 85 | 86 | for (l = scene.lights.length - 1; l >= 0; l--) { 87 | light = scene.lights[l]; 88 | x = Math.randomInRange(this.width*this.minLightX, this.width*this.maxLightX); 89 | y = Math.randomInRange(this.height*this.minLightY, this.height*this.maxLightY); 90 | FSS.Vector3.set(light.position, x, this.height-y, this.lightZ); 91 | FSS.Vector3.subtract(light.position, center); 92 | } 93 | 94 | update(); 95 | render(); 96 | 97 | switch(RENDER.renderer) { 98 | case WEBGL: 99 | window.open(webglRenderer.element.toDataURL(), '_blank'); 100 | break; 101 | case CANVAS: 102 | window.open(canvasRenderer.element.toDataURL(), '_blank'); 103 | break; 104 | case SVG: 105 | var data = encodeURIComponent(output.innerHTML); 106 | var url = "data:image/svg+xml," + data; 107 | window.open(url, '_blank'); 108 | break; 109 | } 110 | 111 | LIGHT.draw = true; 112 | LIGHT.autopilot = autopilot; 113 | LIGHT.zOffset = zOffset; 114 | MESH.depth = depth; 115 | 116 | resize(container.offsetWidth, container.offsetHeight); 117 | } 118 | }; 119 | 120 | //------------------------------ 121 | // Global Properties 122 | //------------------------------ 123 | var now, start = Date.now(); 124 | var center = FSS.Vector3.create(); 125 | var attractor = FSS.Vector3.create(); 126 | var container = document.getElementById('container'); 127 | var output = document.getElementById('output'); 128 | var renderer, scene, mesh, geometry, material; 129 | var webglRenderer, canvasRenderer, svgRenderer; 130 | var gui, autopilotController; 131 | 132 | //------------------------------ 133 | // Methods 134 | //------------------------------ 135 | function initialise() { 136 | createRenderer(); 137 | createScene(); 138 | createMesh(); 139 | createLights(); 140 | addEventListeners(); 141 | resize(container.offsetWidth, container.offsetHeight); 142 | animate(); 143 | } 144 | 145 | function createRenderer() { 146 | webglRenderer = new FSS.WebGLRenderer(); 147 | canvasRenderer = new FSS.CanvasRenderer(); 148 | svgRenderer = new FSS.SVGRenderer(); 149 | setRenderer(RENDER.renderer); 150 | } 151 | 152 | function setRenderer(index) { 153 | if (renderer) { 154 | output.removeChild(renderer.element); 155 | } 156 | switch(index) { 157 | case WEBGL: 158 | renderer = webglRenderer; 159 | break; 160 | case CANVAS: 161 | renderer = canvasRenderer; 162 | break; 163 | case SVG: 164 | renderer = svgRenderer; 165 | break; 166 | } 167 | renderer.setSize(container.offsetWidth, container.offsetHeight); 168 | output.appendChild(renderer.element); 169 | } 170 | 171 | function createScene() { 172 | scene = new FSS.Scene(); 173 | } 174 | 175 | function createMesh() { 176 | scene.remove(mesh); 177 | renderer.clear(); 178 | geometry = new FSS.Plane(MESH.width * renderer.width, MESH.height * renderer.height, MESH.segments, MESH.slices); 179 | material = new FSS.Material(MESH.ambient, MESH.diffuse); 180 | mesh = new FSS.Mesh(geometry, material); 181 | scene.add(mesh); 182 | 183 | // Augment vertices for animation 184 | var v, vertex; 185 | for (v = geometry.vertices.length - 1; v >= 0; v--) { 186 | vertex = geometry.vertices[v]; 187 | vertex.anchor = FSS.Vector3.clone(vertex.position); 188 | vertex.step = FSS.Vector3.create( 189 | Math.randomInRange(0.2, 1.0), 190 | Math.randomInRange(0.2, 1.0), 191 | Math.randomInRange(0.2, 1.0) 192 | ); 193 | vertex.time = Math.randomInRange(0, Math.PIM2); 194 | } 195 | } 196 | 197 | function createLights() { 198 | var l, light; 199 | for (l = scene.lights.length - 1; l >= 0; l--) { 200 | light = scene.lights[l]; 201 | scene.remove(light); 202 | } 203 | renderer.clear(); 204 | for (l = 0; l < LIGHT.count; l++) { 205 | light = new FSS.Light(LIGHT.ambient, LIGHT.diffuse); 206 | light.ambientHex = light.ambient.format(); 207 | light.diffuseHex = light.diffuse.format(); 208 | scene.add(light); 209 | 210 | // Augment light for animation 211 | light.mass = Math.randomInRange(0.5, 1); 212 | light.velocity = FSS.Vector3.create(); 213 | light.acceleration = FSS.Vector3.create(); 214 | light.force = FSS.Vector3.create(); 215 | } 216 | } 217 | 218 | function resize(width, height) { 219 | renderer.setSize(width, height); 220 | FSS.Vector3.set(center, renderer.halfWidth, renderer.halfHeight); 221 | createMesh(); 222 | } 223 | 224 | var paused = false; 225 | 226 | function animate() { 227 | now = Date.now() - start; 228 | update(); 229 | render(); 230 | if (!paused) 231 | requestAnimationFrame(animate); 232 | } 233 | 234 | function pause() { 235 | paused = true; 236 | } 237 | 238 | function resume() { 239 | paused = false; 240 | animate(); 241 | } 242 | 243 | function update() { 244 | var ox, oy, oz, l, light, v, vertex, offset = MESH.depth/2; 245 | 246 | // Update Bounds 247 | FSS.Vector3.copy(LIGHT.bounds, center); 248 | FSS.Vector3.multiplyScalar(LIGHT.bounds, LIGHT.xyScalar); 249 | 250 | // Update Attractor 251 | FSS.Vector3.setZ(attractor, LIGHT.zOffset); 252 | 253 | // Overwrite the Attractor position 254 | if (LIGHT.autopilot) { 255 | ox = Math.sin(LIGHT.step[0] * now * LIGHT.speed); 256 | oy = Math.cos(LIGHT.step[1] * now * LIGHT.speed); 257 | FSS.Vector3.set(attractor, 258 | LIGHT.bounds[0]*ox, 259 | LIGHT.bounds[1]*oy, 260 | LIGHT.zOffset); 261 | } 262 | 263 | // Animate Lights 264 | for (l = scene.lights.length - 1; l >= 0; l--) { 265 | light = scene.lights[l]; 266 | 267 | // Reset the z position of the light 268 | FSS.Vector3.setZ(light.position, LIGHT.zOffset); 269 | 270 | // Calculate the force Luke! 271 | var D = Math.clamp(FSS.Vector3.distanceSquared(light.position, attractor), LIGHT.minDistance, LIGHT.maxDistance); 272 | var F = LIGHT.gravity * light.mass / D; 273 | FSS.Vector3.subtractVectors(light.force, attractor, light.position); 274 | FSS.Vector3.normalise(light.force); 275 | FSS.Vector3.multiplyScalar(light.force, F); 276 | 277 | // Update the light position 278 | FSS.Vector3.set(light.acceleration); 279 | FSS.Vector3.add(light.acceleration, light.force); 280 | FSS.Vector3.add(light.velocity, light.acceleration); 281 | FSS.Vector3.multiplyScalar(light.velocity, LIGHT.dampening); 282 | FSS.Vector3.limit(light.velocity, LIGHT.minLimit, LIGHT.maxLimit); 283 | FSS.Vector3.add(light.position, light.velocity); 284 | } 285 | 286 | // Animate Vertices 287 | for (v = geometry.vertices.length - 1; v >= 0; v--) { 288 | vertex = geometry.vertices[v]; 289 | ox = Math.sin(vertex.time + vertex.step[0] * now * MESH.speed); 290 | oy = Math.cos(vertex.time + vertex.step[1] * now * MESH.speed); 291 | oz = Math.sin(vertex.time + vertex.step[2] * now * MESH.speed); 292 | FSS.Vector3.set(vertex.position, 293 | MESH.xRange*geometry.segmentWidth*ox, 294 | MESH.yRange*geometry.sliceHeight*oy, 295 | MESH.zRange*offset*oz - offset); 296 | FSS.Vector3.add(vertex.position, vertex.anchor); 297 | } 298 | 299 | // Set the Geometry to dirty 300 | geometry.dirty = true; 301 | } 302 | 303 | function render() { 304 | renderer.render(scene); 305 | } 306 | 307 | function addEventListeners() { 308 | window.addEventListener('resize', onWindowResize); 309 | container.addEventListener('mousemove', onMouseMove); 310 | } 311 | 312 | //------------------------------ 313 | // Callbacks 314 | //------------------------------ 315 | function onMouseMove(event) { 316 | FSS.Vector3.set(attractor, event.x, renderer.height - event.y); 317 | FSS.Vector3.subtract(attractor, center); 318 | } 319 | 320 | function onWindowResize(event) { 321 | resize(container.offsetWidth, container.offsetHeight); 322 | render(); 323 | } 324 | 325 | window.bgAnimation = { 326 | pause: pause, 327 | resume: resume 328 | } 329 | 330 | // Let there be light! 331 | initialise(); 332 | 333 | })(); 334 | -------------------------------------------------------------------------------- /css/fontello-embedded.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'fontello'; 3 | src: url('../font/fontello.eot?31072048'); 4 | src: url('../font/fontello.eot?31072048#iefix') format('embedded-opentype'), 5 | url('../font/fontello.svg?31072048#fontello') format('svg'); 6 | font-weight: normal; 7 | font-style: normal; 8 | } 9 | @font-face { 10 | font-family: 'fontello'; 11 | src: url('data:application/octet-stream;base64,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') format('woff'), 12 | url('data:application/octet-stream;base64,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') format('truetype'); 13 | } 14 | /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ 15 | /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ 16 | /* 17 | @media screen and (-webkit-min-device-pixel-ratio:0) { 18 | @font-face { 19 | font-family: 'fontello'; 20 | src: url('../font/fontello.svg?31072048#fontello') format('svg'); 21 | } 22 | } 23 | */ 24 | 25 | [class^="icon-"]:before, [class*=" icon-"]:before { 26 | font-family: "fontello"; 27 | font-style: normal; 28 | font-weight: normal; 29 | speak: none; 30 | 31 | display: inline-block; 32 | text-decoration: inherit; 33 | width: 1em; 34 | margin-right: .2em; 35 | text-align: center; 36 | /* opacity: .8; */ 37 | 38 | /* For safety - reset parent styles, that can break glyph codes*/ 39 | font-variant: normal; 40 | text-transform: none; 41 | 42 | /* fix buttons height, for twitter bootstrap */ 43 | line-height: 1em; 44 | 45 | /* Animation center compensation - margins should be symmetric */ 46 | /* remove if not needed */ 47 | margin-left: .2em; 48 | 49 | /* you can be more comfortable with increased icons size */ 50 | /* font-size: 120%; */ 51 | 52 | /* Uncomment for 3D effect */ 53 | /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ 54 | } 55 | .icon-level-up:before { content: '\e801'; } /* '' */ 56 | .icon-cw-1:before { content: '\e802'; } /* '' */ 57 | .icon-language:before { content: '\e803'; } /* '' */ -------------------------------------------------------------------------------- /client.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) 2013 Mustafa İlhan released under the MIT license 3 | */ 4 | 5 | (function(){ 6 | 7 | //------------------------------ 8 | // Shared 9 | //------------------------------ 10 | var SERVER_URL = "http://shareddeath.nodejitsu.com" 11 | , LANGUAGE = "en" 12 | , SOCKET_TYPE = "tweet_en"; 13 | 14 | window.exitApp = function() {}; 15 | window.refreshApp = function() {}; 16 | //window.bgAnimation = { pause, resume }; 17 | 18 | // Hides mobile browser's address bar when page is done loading. 19 | window.addEventListener('load', function(e) { 20 | setTimeout(function() { window.scrollTo(0, 1); }, 1); 21 | }, false); 22 | 23 | // Check for touch device behaviour and events for touch device 24 | if (!!('ontouchstart' in window)) { 25 | $(".right-icons .icon").css({ display: "none" }); 26 | $(".language-selection").css({ "margin-right": 0, opacity: 1 }); 27 | } 28 | 29 | function _(id) { 30 | return document.getElementById(id); 31 | } 32 | 33 | function changeLanguage(lang) { 34 | if (lang != LANGUAGE) { 35 | LANGUAGE = lang; 36 | if (LANGUAGE == "en") { 37 | SOCKET_TYPE = "tweet_en"; 38 | _('langEN').className = 'language active'; 39 | _('langTR').className = 'language'; 40 | 41 | _('app1Title').innerHTML = "GRAVESTONE"; 42 | _('app2Title').innerHTML = "TWO-DOOR INN"; 43 | _('app3Title').innerHTML = "TOMB"; 44 | _('aboutTitle').innerHTML = "ABOUT"; 45 | _('aboutBody').innerHTML = 'Shared Death is a web-based artwork which filters shared thoughts containing keywords like "death", "dying" from the Twitter in real time. It is purpose is to help people better understand how other people thinks and feels about the shared destiny of all living things in the planet. You can participate in this experiment by sending tweet with #death keyword. Read More
Mustafa İlhan - @mustilica'; 46 | 47 | } else { 48 | SOCKET_TYPE = "tweet_tr"; 49 | _('langTR').className = 'language active'; 50 | _('langEN').className = 'language'; 51 | 52 | _('app1Title').innerHTML = "MEZAR TAŞI"; 53 | _('app2Title').innerHTML = "İKİ KAPILI HAN"; 54 | _('app3Title').innerHTML = "MAKBER"; 55 | _('aboutTitle').innerHTML = "HAKKINDA"; 56 | _('aboutBody').innerHTML = 'Paylaşılan Ölüm gerçek zamanlı olarak Twitter\'da içinde "ölüm", "ölüyorum" gibi anahtar kelimeleri içeren düşüncelerin filtrelenerek gösterildiği web tabanlı bir sanat uygulamasıdır. Amacı kullanıcıya diğer insanların gezegende yaşayan tüm canlıların ortak kaderi -ölüm- hakkında ne düşündüklerini ve nasıl hissettiklerini anlatmaya yardımcı olmaktır. Siz de #ölüm anahtar kelimesini içeren bir tweet atarak bu deneye katılabilirsiniz. Devamı
Mustafa İlhan - @mustilica'; 57 | } 58 | } 59 | } 60 | 61 | //------------------------------ 62 | // Home 63 | //------------------------------ 64 | function openApp(app) { 65 | $("#home").fadeOut("easing-out", function() { 66 | $("#container").fadeOut("easing-out", function() { 67 | window.bgAnimation.pause(); 68 | window.exitApp = app(); 69 | }); 70 | }); 71 | } 72 | 73 | function goBackToHome() { 74 | window.bgAnimation.resume(); 75 | 76 | $("#backIcon, #refreshIcon").css("display", "none"); 77 | $("#rightIcons").css("display", "inline"); 78 | 79 | $("#container").fadeIn("easing-out", function() { 80 | $("#home").fadeIn("easing-out", function() { 81 | 82 | }); 83 | }); 84 | } 85 | 86 | //------------------------------ 87 | // App1 88 | //------------------------------ 89 | function mezarTasi() { 90 | // Display scren 91 | $("#app1").css("display", "block"); 92 | $("#backIcon").css("display", "inline"); 93 | $("#refreshIcon").css("display", "none"); 94 | $("#rightIcons").css("display", "none"); 95 | 96 | var queue = [] 97 | , waiting = true // isWaiting to display a tweet. 98 | , waitTextTimeout 99 | , writeTextTimeout 100 | , textTime = 64 // msec. 101 | , animationTime = 400 // msec. 102 | , tweetList = $('ul') 103 | , listElementHeight = 0 104 | , socket = io.connect(SERVER_URL, {'force new connection':true}) 105 | , maxIndexCount = Math.floor(tweetList.width() / (parseInt($("li").css("font-size").substring(0,2), 10)/2) ) - 1; // TODO Window resizeda tekrar kontrol et. 106 | 107 | /** 108 | * Display first tweet of queue. 109 | */ 110 | function displayTwit() { 111 | if (queue.length) { 112 | var data = queue.shift(); 113 | if (typeof data != 'undefined') { 114 | writeText(data, 1); 115 | waiting = false; 116 | } else { 117 | console.warn("data is undefined."); 118 | } 119 | } else { 120 | // if queue is empty then wait for new tweets. 121 | waiting = true; 122 | } 123 | } 124 | 125 | /** 126 | * Writes tweets to the page. 127 | */ 128 | function writeText(data, index) { 129 | // Stop waitText animation 130 | clearTimeout(waitTextTimeout); 131 | // Get current list element 132 | var current = $("#current"); 133 | 134 | if (typeof current[0] == 'undefined') { 135 | console.warn("current is undefined"); 136 | return; 137 | } 138 | 139 | // Write text 140 | current.text(data.text.substring(0, index)); 141 | 142 | if (index <= data.text.length - 1) { 143 | 144 | // Animtion check for multiple line tweets 145 | if (index % maxIndexCount == 0) { 146 | 147 | if (!listElementHeight) { 148 | // Calculate list element height 149 | listElementHeight = current.outerHeight(); 150 | } 151 | 152 | // If tweet is multiple line animate it. 153 | current.animate({height: current.height() + listElementHeight}, animationTime, function() { 154 | writeTextTimeout = setTimeout(writeText, textTime, data, index+1); 155 | }); 156 | return; 157 | } 158 | 159 | // Call for the next index. 160 | writeTextTimeout = setTimeout(writeText, textTime, data, index+1); 161 | 162 | } else { 163 | // Writing tweet finished. 164 | // Change style and animate list element. 165 | current.removeAttr('id'); 166 | current.delay(animationTime).animate({opacity: .6}, animationTime, function () { 167 | // Insert new one 168 | tweetList.prepend('
  •  
  • '); 169 | $("#current").delay(textTime).slideDown(animationTime, function () { 170 | waitText(true); 171 | displayTwit(); 172 | }); 173 | }); 174 | } 175 | } 176 | 177 | /** 178 | * Waiting animation until new tweet comes. 179 | */ 180 | function waitText(state) { 181 | var current = $("#current"); 182 | state ? current[0].innerHTML = "|" : current[0].innerHTML = " "; 183 | waitTextTimeout = setTimeout(waitText, animationTime, !state); 184 | // TODO fade-in fade-out 185 | } 186 | 187 | /** 188 | * Exit function to stop ongoing executions. 189 | */ 190 | function exit() { 191 | // Disconnect from server. 192 | socket.disconnect(); 193 | 194 | queue = []; 195 | waiting = false; 196 | // Stop current animations 197 | clearTimeout(waitTextTimeout); 198 | clearTimeout(writeTextTimeout); 199 | 200 | // Fade-out screen and open homescreen 201 | $("#app1").fadeOut("easing-out", function() { 202 | // Empty tweets and insert initial 203 | tweetList.empty().append('
  •  
  • '); 204 | goBackToHome(); 205 | }); 206 | } 207 | 208 | // Start 209 | waitText(true); 210 | 211 | // Get tweets from server. 212 | socket.on(SOCKET_TYPE, function (data) { 213 | queue.push(data); 214 | if (waiting && queue.length == 1) { 215 | displayTwit(); 216 | } 217 | }); 218 | 219 | return exit; 220 | } 221 | 222 | //------------------------------ 223 | // App2 224 | //------------------------------ 225 | function ikiKapiliHan() { 226 | // Display scren 227 | $("#app2").css("display", "block"); 228 | $("#backIcon").css("display", "inline"); 229 | $("#refreshIcon").css("display", "none"); 230 | $("#rightIcons").css("display", "none"); 231 | 232 | var queue = [] 233 | , paused = true 234 | , tweetList = $('#tweets-app2') 235 | , socket = io.connect(SERVER_URL, {'force new connection':true}); 236 | 237 | var options = { 238 | duration: 5000 // Time (ms) each blurb will remain on screen 239 | , rearrangeDuration: 1000 // Time (ms) a character takes to reach its position 240 | , effect: 'fadeIn' // Animation effect the characters use to appear 241 | , centered: true // Centers the text relative to its container 242 | }; 243 | 244 | /** 245 | * Exit function to stop ongoing executions. 246 | */ 247 | function exit() { 248 | // Disconnect from server. 249 | socket.disconnect(); 250 | // Stop current animations 251 | tweetList.textualizer('stop'); 252 | 253 | // Fade-out screen and open homescreen 254 | $("#app2").fadeOut("easing-out", goBackToHome); 255 | } 256 | 257 | tweetList.textualizer(queue, options); // textualize it! 258 | 259 | tweetList.on('textualizer.changed', function(event, args) { 260 | // Check if it's the last index in the array 261 | //console.log("args.index: " + args.index + ", queue.length" + queue.length) 262 | if (args.index == queue.length - 1) { 263 | tweetList.textualizer('pause'); 264 | paused = true; 265 | console.log("paused"); 266 | } 267 | }); 268 | 269 | socket.on(SOCKET_TYPE, function (data) { 270 | var text = data.text 271 | queue.push(text); 272 | 273 | if (paused && queue.length < 2) { 274 | tweetList.textualizer('start'); 275 | paused = false; 276 | console.log("started"); 277 | } 278 | }); 279 | 280 | return exit; 281 | } 282 | 283 | //------------------------------ 284 | // App3 285 | //------------------------------ 286 | function makber() { 287 | // Display scren 288 | $("#app3").fadeIn(); 289 | $("#backIcon, #refreshIcon").css("display", "inline"); 290 | $("#rightIcons").css("display", "none"); 291 | 292 | var width = $(window).width() 293 | , height = $(window).height() 294 | , fontSize; 295 | 296 | if (width > 2200) { 297 | fontSize = "58px"; 298 | } else if (width > 1600) { 299 | fontSize = "40px"; 300 | } else if (width > 1024) { 301 | fontSize = "32px"; 302 | } else if (width > 640) { 303 | fontSize = "24px"; 304 | } else { 305 | fontSize = "16px"; 306 | } 307 | 308 | var tweetList = $('#tweets-app3') 309 | , socket = io.connect(SERVER_URL, {'force new connection':true}); 310 | 311 | /** 312 | * Display tweet on the screen 313 | */ 314 | function displayTweet(text) { 315 | var element = jQuery('
    ') 316 | .text(text) 317 | .attr('class', 'tweet') 318 | .css(getPositions(height, width)); 319 | tweetList.prepend(element); 320 | element.animate({'font-size': fontSize, 'letter-spacing': '1px'}, 400); 321 | } 322 | 323 | /** 324 | * Calculate and return the position of tweet. 325 | */ 326 | function getPositions(height, width) { 327 | var direction = Math.floor(Math.random() * 10) % 2 328 | , css = {}; 329 | 330 | // Top or bottom 331 | if (direction) { 332 | css.top = Math.floor((Math.random()*10000) % height); 333 | } else { 334 | css.bottom = Math.floor((Math.random()*10000) % height); 335 | } 336 | 337 | direction = Math.floor(Math.random() * 10) % 2; 338 | // Left or right 339 | if (direction) { 340 | css.left = Math.floor((Math.random()*10000) % width); 341 | } else { 342 | css.right = Math.floor((Math.random()*10000) % width); 343 | } 344 | return css; 345 | } 346 | 347 | /** 348 | * Clears the screen 349 | */ 350 | function clearScreen() { 351 | tweetList.empty(); 352 | } 353 | 354 | /** 355 | * Exit function to stop ongoing executions. 356 | */ 357 | function exit() { 358 | // Disconnect from server. 359 | socket.disconnect(); 360 | 361 | // Fade-out screen and open homescreen 362 | $("#app3").fadeOut("easing-out", function() { 363 | clearScreen(); 364 | goBackToHome(); 365 | }); 366 | } 367 | 368 | window.refreshApp = clearScreen; 369 | 370 | socket.on(SOCKET_TYPE, function (data) { 371 | displayTweet(data.text); 372 | }); 373 | 374 | return exit; 375 | } 376 | 377 | //------------------------------ 378 | // Listeners 379 | //------------------------------ 380 | _('mezarTasi').addEventListener("click", function() { openApp(mezarTasi); }, false); 381 | _('ikiKapiliHan').addEventListener("click", function() { openApp(ikiKapiliHan); }, false); 382 | _('makber').addEventListener("click", function() { openApp(makber); }, false); 383 | 384 | _('backIcon').addEventListener("click", function() { exitApp(); }, false); 385 | _('refreshIcon').addEventListener("click", function() { refreshApp(); }, false); 386 | 387 | _('langEN').addEventListener("click", function() { changeLanguage('en'); }, false); 388 | _('langTR').addEventListener("click", function() { changeLanguage('tr'); }, false); 389 | 390 | })(); 391 | -------------------------------------------------------------------------------- /main.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) 2013 Mustafa İlhan released under the MIT license 3 | */ 4 | 5 | /******************************* 6 | * COMMON 7 | ******************************/ 8 | * { 9 | margin: 0; 10 | padding: 0; 11 | } 12 | *, *:after, *:before { 13 | -webkit-box-sizing: border-box; 14 | -moz-box-sizing: border-box; 15 | box-sizing: border-box; 16 | } 17 | body { 18 | overflow: hidden; 19 | background: #222; 20 | } 21 | a { 22 | color: #F6004F; 23 | text-decoration: none; 24 | } 25 | a:hover { 26 | text-decoration: underline; 27 | } 28 | a:active { 29 | color: #AAA; 30 | } 31 | a:visited, a:click { 32 | color: #F6004F; 33 | } 34 | .background { 35 | position: absolute; 36 | width: 100%; 37 | height: 100%; 38 | } 39 | .vignette { 40 | background-image: -webkit-radial-gradient(50% 50%, ellipse, rgba(0,0,0,0) 40%, rgba(0,0,0,1) 100%); 41 | background-image: radial-gradient(50% 50%, ellipse, rgba(0,0,0,0) 40%, rgba(0,0,0,1) 100%); 42 | } 43 | .noise { 44 | background-image: url("noise.png"); 45 | opacity: 0.075; 46 | } 47 | .full-screen { 48 | pointer-events: none; 49 | position: absolute; 50 | top: 0; 51 | bottom: 0; 52 | left: 0; 53 | right: 0; 54 | } 55 | .warning { 56 | padding: 20px; 57 | color: #ddd; 58 | display: none; 59 | } 60 | .app1, .app2, .app3 { 61 | display: none; 62 | } 63 | .icons { 64 | pointer-events: auto; 65 | font-size: 24px; 66 | color: #fff; 67 | } 68 | .left-icons, .right-icons { 69 | position: absolute; 70 | z-index: 999; 71 | } 72 | .left-icons { 73 | cursor: pointer; 74 | top: 25px; 75 | left: 65px; 76 | } 77 | #backIcon, #refreshIcon { 78 | display: none; 79 | } 80 | .right-icons { 81 | top: 25px; 82 | right: 65px; 83 | } 84 | .icon { 85 | padding: 5px; 86 | margin: 0; 87 | color: #fff; 88 | opacity: .4; 89 | } 90 | .left-icons .icon:hover { 91 | opacity: 1; 92 | } 93 | .right-icons:hover .icon { 94 | opacity: 1; 95 | } 96 | .language-selection { 97 | font-size: 12px; 98 | letter-spacing: .1em; 99 | font-family: 'Open Sans', sans-serif; 100 | font-weight: 300px; 101 | display: inline-block; 102 | margin-right: -200px; 103 | opacity: 0; 104 | -webkit-transition: margin-right 0.3s ease-out, opacity 0.3s ease-out 0.2s; 105 | -moz-transition: margin-right 0.3s ease-out, opacity 0.3s ease-out 0.2s; 106 | -o-transition: margin-right 0.3s ease-out, opacity 0.3s ease-out 0.2s; 107 | -ms-transition: margin-right 0.3s ease-out, opacity 0.3s ease-out 0.2s; 108 | transition: margin-right 0.3s ease-out, opacity 0.3s ease-out 0.2s; 109 | } 110 | .right-icons:hover .language-selection { 111 | margin-right: 0; 112 | opacity: 1; 113 | } 114 | .language { 115 | display: inline-block; 116 | cursor: pointer; 117 | padding: 5px 7px; 118 | } 119 | .language:hover { 120 | background: #999; 121 | color: #222; 122 | } 123 | .language.active { 124 | background: #555; 125 | color: #fff; 126 | cursor: default; 127 | } 128 | 129 | /******************************* 130 | * HOME 131 | ******************************/ 132 | .home { 133 | color: #ddd; 134 | line-height: 16px; 135 | font-size: 12px; 136 | font-weight: 300; 137 | font-family: 'Open Sans', sans-serif; 138 | } 139 | .links { 140 | position: absolute; 141 | top: 50%; 142 | left: 0; 143 | right: 0; 144 | margin-top: -77px; 145 | height: 154px; 146 | text-align: center; 147 | pointer-events: auto; 148 | } 149 | .row { 150 | padding: 10px 0 0; 151 | overflow: hidden; 152 | cursor: pointer; 153 | height: 40px; 154 | } 155 | .row, .title, .icon, .language, .about, p { 156 | -webkit-transition: all 0.3s ease-out; 157 | -moz-transition: all 0.3s ease-out; 158 | -o-transition: all 0.3s ease-out; 159 | -ms-transition: all 0.3s ease-out; 160 | transition: all 0.3s ease-out; 161 | } 162 | .title { 163 | font-size: 24px; 164 | line-height: 28px; 165 | letter-spacing: .3em; 166 | } 167 | .row:hover { 168 | transform: scale(1.2); 169 | -ms-transform: scale(1.2); 170 | -webkit-transform: scale(1.2); 171 | } 172 | .row:hover .title { 173 | letter-spacing: .6em; 174 | } 175 | .about { 176 | position: absolute; 177 | bottom: 0px; 178 | left: 50%; 179 | margin-left: -230px; 180 | padding-bottom: 30px; 181 | width: 460px; 182 | height: 42px; 183 | cursor: pointer; 184 | text-align: center; 185 | overflow: hidden; 186 | pointer-events: auto; 187 | letter-spacing: .1em; 188 | } 189 | .about:hover { 190 | height: 160px; 191 | } 192 | .about p { 193 | opacity: 0; 194 | -webkit-transition-delay: 0.2s; 195 | -moz-transition-delay: 0.2s; 196 | -o-transition-delay: 0.2s; 197 | -ms-transition-delay: 0.2s; 198 | transition-delay: 0.2s; 199 | } 200 | .about:hover p { 201 | opacity: 1; 202 | } 203 | hr { 204 | height: 10px; 205 | font-size: 10px; 206 | border: none; 207 | color: #888; 208 | } 209 | hr:after { 210 | content: ".................................................................................................................................................................................................."; 211 | letter-spacing: 10px; 212 | } 213 | 214 | /******************************* 215 | * APP1 216 | ******************************/ 217 | .app1 { 218 | color: #F6004F; 219 | overflow-y: scroll; 220 | overflow-x: hidden; 221 | font-size: 12px; 222 | font-weight: 400; 223 | font-family: 'Ubuntu Mono', sans-serif; 224 | } 225 | ul { 226 | list-style-type: none; 227 | margin: 75px; 228 | } 229 | li { 230 | padding: 20px 0; 231 | font-size: 24px; 232 | line-height: 30px; 233 | border-bottom: 1px solid #1a1a1a; 234 | } 235 | li:last-child { 236 | border-bottom: none; 237 | } 238 | .search-key { 239 | color: #222; 240 | background-color: #F6004F; 241 | } 242 | #current { 243 | display: none; 244 | } 245 | ::-webkit-scrollbar { 246 | width: 10px; 247 | height: 10px; 248 | background-color: #222; 249 | } 250 | ::-webkit-scrollbar-button:start:decrement, 251 | ::-webkit-scrollbar-button:end:increment { 252 | display: none; 253 | } 254 | ::-webkit-scrollbar-track-piece { 255 | background-color: #3b3b3b; 256 | -webkit-border-radius: 6px; 257 | } 258 | ::-webkit-scrollbar-thumb:vertical { 259 | background-color: #555; 260 | -webkit-border-radius: 6px; 261 | } 262 | 263 | /******************************* 264 | * APP2 265 | ******************************/ 266 | .app2 { 267 | color: #F6004F; 268 | font-size: 12px; 269 | font-weight: 700; 270 | font-family: 'Open Sans', sans-serif; 271 | } 272 | #tweets-app2 { 273 | padding: 20px 75px; 274 | font-size: 5em; 275 | } 276 | 277 | /******************************* 278 | * APP3 279 | ******************************/ 280 | .app3 { 281 | color: #222; 282 | background: #fff; 283 | font-weight: 700; 284 | font-family: 'Open Sans', sans-serif; 285 | } 286 | #tweets-app3 { 287 | 288 | } 289 | .tweet { 290 | position: absolute; 291 | font-size: 120px; 292 | letter-spacing: 20px; 293 | } 294 | 295 | /******************************* 296 | * MEDIA QUERIES 297 | ******************************/ 298 | 299 | @media only screen and (min-width: 240px) and (max-width: 319px) { 300 | 301 | } 302 | 303 | @media only screen and (min-width: 240px) and (max-width: 319px) and (orientation : landscape) { 304 | 305 | } 306 | 307 | @media only screen and (min-width: 240px) and (max-width: 319px) and (orientation : portrait) { 308 | 309 | } 310 | 311 | @media only screen and (min-width: 240px) and (max-width: 479px) { 312 | .icons { 313 | font-size: 14px; 314 | } 315 | .icon { 316 | opacity: .6; 317 | } 318 | .left-icons { 319 | top: 10px; 320 | left: 18px; 321 | } 322 | .right-icons { 323 | top: 10px; 324 | right: 18px; 325 | } 326 | .right-icons .icon { 327 | display: none; 328 | } 329 | .language-selection { 330 | margin-right: 0; 331 | opacity: 1; 332 | } 333 | .title { 334 | font-size: 14px; 335 | line-height: 20px; 336 | } 337 | .about { 338 | display: none; 339 | } 340 | ul { 341 | margin: 30px 20px; 342 | } 343 | li { 344 | padding: 10px 0; 345 | font-size: 14px; 346 | line-height: 18px; 347 | } 348 | #tweets-app2 { 349 | padding: 10px 20px; 350 | font-size: 2em; 351 | } 352 | .tweet { 353 | font-size: 60px; 354 | letter-spacing: 10px; 355 | } 356 | } 357 | 358 | @media only screen and (min-width: 480px) and (max-width: 639px) { 359 | .icons { 360 | font-size: 16px; 361 | } 362 | .icon { 363 | opacity: .6; 364 | } 365 | .left-icons { 366 | top: 10px; 367 | left: 18px; 368 | } 369 | .right-icons { 370 | top: 10px; 371 | right: 18px; 372 | } 373 | .right-icons .icon { 374 | display: none; 375 | } 376 | .language-selection { 377 | font-size: 12px; 378 | margin-right: 0; 379 | opacity: 1; 380 | } 381 | .about { 382 | width: 90%; 383 | margin-left: -45%; 384 | } 385 | ul { 386 | margin: 30px 20px; 387 | } 388 | li { 389 | padding: 10px 0; 390 | font-size: 16px; 391 | line-height: 22px; 392 | } 393 | #tweets-app2 { 394 | padding: 10px 20px; 395 | font-size: 3em; 396 | } 397 | .tweet { 398 | font-size: 60px; 399 | letter-spacing: 10px; 400 | } 401 | } 402 | 403 | @media only screen and (min-width: 480px) and (max-width: 639px) and (orientation : landscape) { 404 | .left-icons { 405 | left: 25px; 406 | } 407 | .right-icons { 408 | right: 25px; 409 | } 410 | ul { 411 | margin: 35px 25px; 412 | } 413 | #tweets-app2 { 414 | padding: 10px 25px; 415 | } 416 | } 417 | 418 | @media only screen and (min-width: 480px) and (max-width: 639px) and (orientation : portrait) { 419 | 420 | } 421 | 422 | @media only screen and (min-width: 640px) and (max-width: 799px) { 423 | .icons { 424 | font-size: 20px; 425 | } 426 | .icon { 427 | opacity: .6; 428 | } 429 | .left-icons { 430 | top: 15px; 431 | left: 25px; 432 | } 433 | .right-icons { 434 | top: 15px; 435 | right: 25px; 436 | } 437 | .language-selection { 438 | font-size: 14px; 439 | } 440 | ul { 441 | margin: 35px 25px; 442 | } 443 | li { 444 | padding: 10px 0; 445 | font-size: 18px; 446 | line-height: 24px; 447 | } 448 | #tweets-app2 { 449 | padding: 10px 25px; 450 | } 451 | .tweet { 452 | font-size: 80px; 453 | letter-spacing: 13px; 454 | } 455 | } 456 | 457 | @media only screen and (min-width: 640px) and (max-width: 799px) and (orientation : landscape) { 458 | .left-icons { 459 | left: 30px; 460 | } 461 | .right-icons { 462 | right: 30px; 463 | } 464 | ul { 465 | margin: 40px 30px; 466 | } 467 | #tweets-app2 { 468 | padding: 10px 30px; 469 | font-size: 3em; 470 | } 471 | } 472 | 473 | @media only screen and (min-width: 640px) and (max-width: 799px) and (orientation : portrait) { 474 | 475 | } 476 | 477 | @media only screen and (min-width: 800px) and (max-width: 1024px) { 478 | .icons { 479 | font-size: 22px; 480 | } 481 | .icon { 482 | opacity: .6; 483 | } 484 | .left-icons { 485 | top: 20px; 486 | left: 30px; 487 | } 488 | .right-icons { 489 | top: 20px; 490 | right: 30px; 491 | } 492 | .language-selection { 493 | font-size: 16px; 494 | } 495 | ul { 496 | margin: 40px 30px; 497 | } 498 | li { 499 | padding: 15px 0; 500 | font-size: 20px; 501 | line-height: 26px; 502 | } 503 | #tweets-app2 { 504 | padding: 10px 30px; 505 | font-size: 4em; 506 | } 507 | .tweet { 508 | font-size: 100px; 509 | letter-spacing: 16px; 510 | } 511 | } 512 | 513 | @media only screen and (min-width: 1025px) and (max-width: 1600px) { 514 | 515 | } 516 | 517 | @media only screen and (min-width: 1600px) and (max-width: 2499px) { 518 | .icons { 519 | font-size: 36px; 520 | } 521 | .left-icons { 522 | top: 50px; 523 | left: 108px; 524 | } 525 | .right-icons { 526 | top: 50px; 527 | right: 108px; 528 | } 529 | .language-selection { 530 | font-size: 16px; 531 | } 532 | .home { 533 | font-size: 16px; 534 | line-height: 24px; 535 | } 536 | .links { 537 | margin-top: -110px; 538 | height: 220px; 539 | } 540 | .row { 541 | padding: 20px 0 0; 542 | height: 60px; 543 | } 544 | .title { 545 | font-size: 36px; 546 | line-height: 46px; 547 | letter-spacing: .4em; 548 | } 549 | .about { 550 | height: 66px; 551 | padding-bottom: 50px; 552 | width: 620px; 553 | margin-left: -310px; 554 | } 555 | .about:hover { 556 | height: 260px; 557 | } 558 | hr { 559 | height: 15px; 560 | font-size: 15px; 561 | } 562 | hr:after { 563 | letter-spacing: 15px; 564 | } 565 | ul { 566 | margin: 120px; 567 | } 568 | li { 569 | font-size: 28px; 570 | line-height: 36px; 571 | padding: 25px 0; 572 | } 573 | #tweets-app2 { 574 | padding: 20px 120px; 575 | font-size: 6.5em; 576 | } 577 | .tweet { 578 | font-size: 180px; 579 | letter-spacing: 30px; 580 | } 581 | } 582 | 583 | @media only screen and (min-width: 2500px) { 584 | .icons { 585 | font-size: 48px; 586 | } 587 | .left-icons { 588 | top: 60px; 589 | left: 130px; 590 | } 591 | .right-icons { 592 | top: 60px; 593 | right: 130px; 594 | } 595 | .language-selection { 596 | font-size: 24px; 597 | } 598 | .home { 599 | font-size: 24px; 600 | line-height: 32px; 601 | } 602 | .links { 603 | margin-top: -140px; 604 | height: 280px; 605 | } 606 | .row { 607 | padding: 20px 0 0; 608 | height: 80px; 609 | } 610 | .title { 611 | font-size: 48px; 612 | line-height: 60px; 613 | letter-spacing: .4em; 614 | } 615 | .about { 616 | height: 100px; 617 | padding-bottom: 70px; 618 | width: 800px; 619 | margin-left: -400px; 620 | } 621 | .about:hover { 622 | height: 400px; 623 | } 624 | hr { 625 | height: 20px; 626 | font-size: 20px; 627 | } 628 | hr:after { 629 | letter-spacing: 20px; 630 | } 631 | ul { 632 | margin: 150px; 633 | } 634 | li { 635 | font-size: 32px; 636 | line-height: 40px; 637 | padding: 30px 0; 638 | } 639 | #tweets-app2 { 640 | padding: 20px 150px; 641 | font-size: 8em; 642 | } 643 | .tweet { 644 | font-size: 240px; 645 | letter-spacing: 40px; 646 | } 647 | } 648 | -------------------------------------------------------------------------------- /fss.min.js: -------------------------------------------------------------------------------- 1 | //============================================================ 2 | // 3 | // Copyright (C) 2013 Matthew Wagerfield 4 | // 5 | // Twitter: https://twitter.com/mwagerfield 6 | // 7 | // Permission is hereby granted, free of charge, to any 8 | // person obtaining a copy of this software and associated 9 | // documentation files (the "Software"), to deal in the 10 | // Software without restriction, including without limitation 11 | // the rights to use, copy, modify, merge, publish, distribute, 12 | // sublicense, and/or sell copies of the Software, and to 13 | // permit persons to whom the Software is furnished to do 14 | // so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice 17 | // shall be included in all copies or substantial portions 18 | // of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY 21 | // OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 22 | // LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 23 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO 24 | // EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 25 | // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 26 | // AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 28 | // OR OTHER DEALINGS IN THE SOFTWARE. 29 | // 30 | //============================================================ 31 | FSS={FRONT:0,BACK:1,DOUBLE:2,SVGNS:"http://www.w3.org/2000/svg"},FSS.Array="function"==typeof Float32Array?Float32Array:Array,FSS.Utils={isNumber:function(t){return!isNaN(parseFloat(t))&&isFinite(t)}},function(){for(var t=0,e=["ms","moz","webkit","o"],i=0;e.length>i&&!window.requestAnimationFrame;++i)window.requestAnimationFrame=window[e[i]+"RequestAnimationFrame"],window.cancelAnimationFrame=window[e[i]+"CancelAnimationFrame"]||window[e[i]+"CancelRequestAnimationFrame"];window.requestAnimationFrame||(window.requestAnimationFrame=function(e){var i=(new Date).getTime(),r=Math.max(0,16-(i-t)),s=window.setTimeout(function(){e(i+r)},r);return t=i+r,s}),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(t){clearTimeout(t)})}(),Math.PIM2=2*Math.PI,Math.PID2=Math.PI/2,Math.randomInRange=function(t,e){return t+(e-t)*Math.random()},Math.clamp=function(t,e,i){return t=Math.max(t,e),t=Math.min(t,i)},FSS.Vector3={create:function(t,e,i){var r=new FSS.Array(3);return this.set(r,t,e,i),r},clone:function(t){var e=this.create();return this.copy(e,t),e},set:function(t,e,i,r){return t[0]=e||0,t[1]=i||0,t[2]=r||0,this},setX:function(t,e){return t[0]=e||0,this},setY:function(t,e){return t[1]=e||0,this},setZ:function(t,e){return t[2]=e||0,this},copy:function(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],this},add:function(t,e){return t[0]+=e[0],t[1]+=e[1],t[2]+=e[2],this},addVectors:function(t,e,i){return t[0]=e[0]+i[0],t[1]=e[1]+i[1],t[2]=e[2]+i[2],this},addScalar:function(t,e){return t[0]+=e,t[1]+=e,t[2]+=e,this},subtract:function(t,e){return t[0]-=e[0],t[1]-=e[1],t[2]-=e[2],this},subtractVectors:function(t,e,i){return t[0]=e[0]-i[0],t[1]=e[1]-i[1],t[2]=e[2]-i[2],this},subtractScalar:function(t,e){return t[0]-=e,t[1]-=e,t[2]-=e,this},multiply:function(t,e){return t[0]*=e[0],t[1]*=e[1],t[2]*=e[2],this},multiplyVectors:function(t,e,i){return t[0]=e[0]*i[0],t[1]=e[1]*i[1],t[2]=e[2]*i[2],this},multiplyScalar:function(t,e){return t[0]*=e,t[1]*=e,t[2]*=e,this},divide:function(t,e){return t[0]/=e[0],t[1]/=e[1],t[2]/=e[2],this},divideVectors:function(t,e,i){return t[0]=e[0]/i[0],t[1]=e[1]/i[1],t[2]=e[2]/i[2],this},divideScalar:function(t,e){return 0!==e?(t[0]/=e,t[1]/=e,t[2]/=e):(t[0]=0,t[1]=0,t[2]=0),this},cross:function(t,e){var i=t[0],r=t[1],s=t[2];return t[0]=r*e[2]-s*e[1],t[1]=s*e[0]-i*e[2],t[2]=i*e[1]-r*e[0],this},crossVectors:function(t,e,i){return t[0]=e[1]*i[2]-e[2]*i[1],t[1]=e[2]*i[0]-e[0]*i[2],t[2]=e[0]*i[1]-e[1]*i[0],this},min:function(t,e){return e>t[0]&&(t[0]=e),e>t[1]&&(t[1]=e),e>t[2]&&(t[2]=e),this},max:function(t,e){return t[0]>e&&(t[0]=e),t[1]>e&&(t[1]=e),t[2]>e&&(t[2]=e),this},clamp:function(t,e,i){return this.min(t,e),this.max(t,i),this},limit:function(t,e,i){var r=this.length(t);return null!==e&&e>r?this.setLength(t,e):null!==i&&r>i&&this.setLength(t,i),this},dot:function(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]},normalise:function(t){return this.divideScalar(t,this.length(t))},negate:function(t){return this.multiplyScalar(t,-1)},distanceSquared:function(t,e){var i=t[0]-e[0],r=t[1]-e[1],s=t[2]-e[2];return i*i+r*r+s*s},distance:function(t,e){return Math.sqrt(this.distanceSquared(t,e))},lengthSquared:function(t){return t[0]*t[0]+t[1]*t[1]+t[2]*t[2]},length:function(t){return Math.sqrt(this.lengthSquared(t))},setLength:function(t,e){var i=this.length(t);return 0!==i&&e!==i&&this.multiplyScalar(t,e/i),this}},FSS.Vector4={create:function(t,e,i){var r=new FSS.Array(4);return this.set(r,t,e,i),r},set:function(t,e,i,r,s){return t[0]=e||0,t[1]=i||0,t[2]=r||0,t[3]=s||0,this},setX:function(t,e){return t[0]=e||0,this},setY:function(t,e){return t[1]=e||0,this},setZ:function(t,e){return t[2]=e||0,this},setW:function(t,e){return t[3]=e||0,this},add:function(t,e){return t[0]+=e[0],t[1]+=e[1],t[2]+=e[2],t[3]+=e[3],this},multiplyVectors:function(t,e,i){return t[0]=e[0]*i[0],t[1]=e[1]*i[1],t[2]=e[2]*i[2],t[3]=e[3]*i[3],this},multiplyScalar:function(t,e){return t[0]*=e,t[1]*=e,t[2]*=e,t[3]*=e,this},min:function(t,e){return e>t[0]&&(t[0]=e),e>t[1]&&(t[1]=e),e>t[2]&&(t[2]=e),e>t[3]&&(t[3]=e),this},max:function(t,e){return t[0]>e&&(t[0]=e),t[1]>e&&(t[1]=e),t[2]>e&&(t[2]=e),t[3]>e&&(t[3]=e),this},clamp:function(t,e,i){return this.min(t,e),this.max(t,i),this}},FSS.Color=function(t,e){this.rgba=FSS.Vector4.create(),this.hex=t||"#000000",this.opacity=FSS.Utils.isNumber(e)?e:1,this.set(this.hex,this.opacity)},FSS.Color.prototype={set:function(t,e){t=t.replace("#","");var i=t.length/3;return this.rgba[0]=parseInt(t.substring(0*i,1*i),16)/255,this.rgba[1]=parseInt(t.substring(1*i,2*i),16)/255,this.rgba[2]=parseInt(t.substring(2*i,3*i),16)/255,this.rgba[3]=FSS.Utils.isNumber(e)?e:this.rgba[3],this},hexify:function(t){var e=Math.ceil(255*t).toString(16);return 1===e.length&&(e="0"+e),e},format:function(){var t=this.hexify(this.rgba[0]),e=this.hexify(this.rgba[1]),i=this.hexify(this.rgba[2]);return this.hex="#"+t+e+i,this.hex}},FSS.Object=function(){this.position=FSS.Vector3.create()},FSS.Object.prototype={setPosition:function(t,e,i){return FSS.Vector3.set(this.position,t,e,i),this}},FSS.Light=function(t,e){FSS.Object.call(this),this.ambient=new FSS.Color(t||"#FFFFFF"),this.diffuse=new FSS.Color(e||"#FFFFFF"),this.ray=FSS.Vector3.create()},FSS.Light.prototype=Object.create(FSS.Object.prototype),FSS.Vertex=function(t,e,i){this.position=FSS.Vector3.create(t,e,i)},FSS.Vertex.prototype={setPosition:function(t,e,i){return FSS.Vector3.set(this.position,t,e,i),this}},FSS.Triangle=function(t,e,i){this.a=t||new FSS.Vertex,this.b=e||new FSS.Vertex,this.c=i||new FSS.Vertex,this.vertices=[this.a,this.b,this.c],this.u=FSS.Vector3.create(),this.v=FSS.Vector3.create(),this.centroid=FSS.Vector3.create(),this.normal=FSS.Vector3.create(),this.color=new FSS.Color,this.polygon=document.createElementNS(FSS.SVGNS,"polygon"),this.polygon.setAttributeNS(null,"stroke-linejoin","round"),this.polygon.setAttributeNS(null,"stroke-miterlimit","1"),this.polygon.setAttributeNS(null,"stroke-width","1"),this.computeCentroid(),this.computeNormal()},FSS.Triangle.prototype={computeCentroid:function(){return this.centroid[0]=this.a.position[0]+this.b.position[0]+this.c.position[0],this.centroid[1]=this.a.position[1]+this.b.position[1]+this.c.position[1],this.centroid[2]=this.a.position[2]+this.b.position[2]+this.c.position[2],FSS.Vector3.divideScalar(this.centroid,3),this},computeNormal:function(){return FSS.Vector3.subtractVectors(this.u,this.b.position,this.a.position),FSS.Vector3.subtractVectors(this.v,this.c.position,this.a.position),FSS.Vector3.crossVectors(this.normal,this.u,this.v),FSS.Vector3.normalise(this.normal),this}},FSS.Geometry=function(){this.vertices=[],this.triangles=[],this.dirty=!1},FSS.Geometry.prototype={update:function(){if(this.dirty){var t,e;for(t=this.triangles.length-1;t>=0;t--)e=this.triangles[t],e.computeCentroid(),e.computeNormal();this.dirty=!1}return this}},FSS.Plane=function(t,e,i,r){FSS.Geometry.call(this),this.width=t||100,this.height=e||100,this.segments=i||4,this.slices=r||4,this.segmentWidth=this.width/this.segments,this.sliceHeight=this.height/this.slices;var s,n,o,h,a,l,u,c=[],S=this.width*-.5,f=.5*this.height;for(s=0;this.segments>=s;s++)for(c.push([]),n=0;this.slices>=n;n++)u=new FSS.Vertex(S+s*this.segmentWidth,f-n*this.sliceHeight),c[s].push(u),this.vertices.push(u);for(s=0;this.segments>s;s++)for(n=0;this.slices>n;n++)o=c[s+0][n+0],h=c[s+0][n+1],a=c[s+1][n+0],l=c[s+1][n+1],t0=new FSS.Triangle(o,h,a),t1=new FSS.Triangle(a,h,l),this.triangles.push(t0,t1)},FSS.Plane.prototype=Object.create(FSS.Geometry.prototype),FSS.Material=function(t,e){this.ambient=new FSS.Color(t||"#444444"),this.diffuse=new FSS.Color(e||"#FFFFFF"),this.slave=new FSS.Color},FSS.Mesh=function(t,e){FSS.Object.call(this),this.geometry=t||new FSS.Geometry,this.material=e||new FSS.Material,this.side=FSS.FRONT,this.visible=!0},FSS.Mesh.prototype=Object.create(FSS.Object.prototype),FSS.Mesh.prototype.update=function(t,e){var i,r,s,n,o;if(this.geometry.update(),e)for(i=this.geometry.triangles.length-1;i>=0;i--){for(r=this.geometry.triangles[i],FSS.Vector4.set(r.color.rgba),s=t.length-1;s>=0;s--)n=t[s],FSS.Vector3.subtractVectors(n.ray,n.position,r.centroid),FSS.Vector3.normalise(n.ray),o=FSS.Vector3.dot(r.normal,n.ray),this.side===FSS.FRONT?o=Math.max(o,0):this.side===FSS.BACK?o=Math.abs(Math.min(o,0)):this.side===FSS.DOUBLE&&(o=Math.max(Math.abs(o),0)),FSS.Vector4.multiplyVectors(this.material.slave.rgba,this.material.ambient.rgba,n.ambient.rgba),FSS.Vector4.add(r.color.rgba,this.material.slave.rgba),FSS.Vector4.multiplyVectors(this.material.slave.rgba,this.material.diffuse.rgba,n.diffuse.rgba),FSS.Vector4.multiplyScalar(this.material.slave.rgba,o),FSS.Vector4.add(r.color.rgba,this.material.slave.rgba);FSS.Vector4.clamp(r.color.rgba,0,1)}return this},FSS.Scene=function(){this.meshes=[],this.lights=[]},FSS.Scene.prototype={add:function(t){return t instanceof FSS.Mesh&&!~this.meshes.indexOf(t)?this.meshes.push(t):t instanceof FSS.Light&&!~this.lights.indexOf(t)&&this.lights.push(t),this},remove:function(t){return t instanceof FSS.Mesh&&~this.meshes.indexOf(t)?this.meshes.splice(this.meshes.indexOf(t),1):t instanceof FSS.Light&&~this.lights.indexOf(t)&&this.lights.splice(this.lights.indexOf(t),1),this}},FSS.Renderer=function(){this.width=0,this.height=0,this.halfWidth=0,this.halfHeight=0},FSS.Renderer.prototype={setSize:function(t,e){return this.width!==t||this.height!==e?(this.width=t,this.height=e,this.halfWidth=.5*this.width,this.halfHeight=.5*this.height,this):void 0},clear:function(){return this},render:function(){return this}},FSS.CanvasRenderer=function(){FSS.Renderer.call(this),this.element=document.createElement("canvas"),this.element.style.display="block",this.context=this.element.getContext("2d"),this.setSize(this.element.width,this.element.height)},FSS.CanvasRenderer.prototype=Object.create(FSS.Renderer.prototype),FSS.CanvasRenderer.prototype.setSize=function(t,e){return FSS.Renderer.prototype.setSize.call(this,t,e),this.element.width=t,this.element.height=e,this.context.setTransform(1,0,0,-1,this.halfWidth,this.halfHeight),this},FSS.CanvasRenderer.prototype.clear=function(){return FSS.Renderer.prototype.clear.call(this),this.context.clearRect(-this.halfWidth,-this.halfHeight,this.width,this.height),this},FSS.CanvasRenderer.prototype.render=function(t){FSS.Renderer.prototype.render.call(this,t);var e,i,r,s,n;for(this.clear(),this.context.lineJoin="round",this.context.lineWidth=1,e=t.meshes.length-1;e>=0;e--)if(i=t.meshes[e],i.visible)for(i.update(t.lights,!0),r=i.geometry.triangles.length-1;r>=0;r--)s=i.geometry.triangles[r],n=s.color.format(),this.context.beginPath(),this.context.moveTo(s.a.position[0],s.a.position[1]),this.context.lineTo(s.b.position[0],s.b.position[1]),this.context.lineTo(s.c.position[0],s.c.position[1]),this.context.closePath(),this.context.strokeStyle=n,this.context.fillStyle=n,this.context.stroke(),this.context.fill();return this},FSS.WebGLRenderer=function(){FSS.Renderer.call(this),this.element=document.createElement("canvas"),this.element.style.display="block",this.vertices=null,this.lights=null;var t={preserveDrawingBuffer:!1,premultipliedAlpha:!0,antialias:!0,stencil:!0,alpha:!0};return this.gl=this.getContext(this.element,t),this.unsupported=!this.gl,this.unsupported?"WebGL is not supported by your browser.":(this.gl.clearColor(0,0,0,0),this.gl.enable(this.gl.DEPTH_TEST),this.setSize(this.element.width,this.element.height),void 0)},FSS.WebGLRenderer.prototype=Object.create(FSS.Renderer.prototype),FSS.WebGLRenderer.prototype.getContext=function(t,e){var i=!1;try{if(!(i=t.getContext("experimental-webgl",e)))throw"Error creating WebGL context."}catch(r){console.error(r)}return i},FSS.WebGLRenderer.prototype.setSize=function(t,e){return FSS.Renderer.prototype.setSize.call(this,t,e),this.unsupported?void 0:(this.element.width=t,this.element.height=e,this.gl.viewport(0,0,t,e),this)},FSS.WebGLRenderer.prototype.clear=function(){return FSS.Renderer.prototype.clear.call(this),this.unsupported?void 0:(this.gl.clear(this.gl.COLOR_BUFFER_BIT|this.gl.DEPTH_BUFFER_BIT),this)},FSS.WebGLRenderer.prototype.render=function(t){if(FSS.Renderer.prototype.render.call(this,t),!this.unsupported){var e,i,r,s,n,o,h,a,l,u,c,S,f,m,g,d=!1,p=t.lights.length,F=0;if(this.clear(),this.lights!==p){if(this.lights=p,!(this.lights>0))return;this.buildProgram(p)}if(this.program){for(e=t.meshes.length-1;e>=0;e--)i=t.meshes[e],i.geometry.dirty&&(d=!0),i.update(t.lights,!1),F+=3*i.geometry.triangles.length;if(d||this.vertices!==F){this.vertices=F;for(a in this.program.attributes){for(u=this.program.attributes[a],u.data=new FSS.Array(F*u.size),f=0,e=t.meshes.length-1;e>=0;e--)for(i=t.meshes[e],r=0,s=i.geometry.triangles.length;s>r;r++)for(n=i.geometry.triangles[r],m=0,g=n.vertices.length;g>m;m++){switch(vertex=n.vertices[m],a){case"side":this.setBufferData(f,u,i.side);break;case"position":this.setBufferData(f,u,vertex.position);break;case"centroid":this.setBufferData(f,u,n.centroid);break;case"normal":this.setBufferData(f,u,n.normal);break;case"ambient":this.setBufferData(f,u,i.material.ambient.rgba);break;case"diffuse":this.setBufferData(f,u,i.material.diffuse.rgba)}f++}this.gl.bindBuffer(this.gl.ARRAY_BUFFER,u.buffer),this.gl.bufferData(this.gl.ARRAY_BUFFER,u.data,this.gl.DYNAMIC_DRAW),this.gl.enableVertexAttribArray(u.location),this.gl.vertexAttribPointer(u.location,u.size,this.gl.FLOAT,!1,0,0)}}for(this.setBufferData(0,this.program.uniforms.resolution,[this.width,this.height,this.width]),o=p-1;o>=0;o--)h=t.lights[o],this.setBufferData(o,this.program.uniforms.lightPosition,h.position),this.setBufferData(o,this.program.uniforms.lightAmbient,h.ambient.rgba),this.setBufferData(o,this.program.uniforms.lightDiffuse,h.diffuse.rgba);for(l in this.program.uniforms)switch(u=this.program.uniforms[l],S=u.location,c=u.data,u.structure){case"3f":this.gl.uniform3f(S,c[0],c[1],c[2]);break;case"3fv":this.gl.uniform3fv(S,c);break;case"4fv":this.gl.uniform4fv(S,c)}}return this.gl.drawArrays(this.gl.TRIANGLES,0,this.vertices),this}},FSS.WebGLRenderer.prototype.setBufferData=function(t,e,i){if(FSS.Utils.isNumber(i))e.data[t*e.size]=i;else for(var r=i.length-1;r>=0;r--)e.data[t*e.size+r]=i[r]},FSS.WebGLRenderer.prototype.buildProgram=function(t){if(!this.unsupported){var e=FSS.WebGLRenderer.VS(t),i=FSS.WebGLRenderer.FS(t),r=e+i;if(!this.program||this.program.code!==r){var s=this.gl.createProgram(),n=this.buildShader(this.gl.VERTEX_SHADER,e),o=this.buildShader(this.gl.FRAGMENT_SHADER,i);if(this.gl.attachShader(s,n),this.gl.attachShader(s,o),this.gl.linkProgram(s),!this.gl.getProgramParameter(s,this.gl.LINK_STATUS)){var h=this.gl.getError(),a=this.gl.getProgramParameter(s,this.gl.VALIDATE_STATUS);return console.error("Could not initialise shader.\nVALIDATE_STATUS: "+a+"\nERROR: "+h),null}return this.gl.deleteShader(o),this.gl.deleteShader(n),s.code=r,s.attributes={side:this.buildBuffer(s,"attribute","aSide",1,"f"),position:this.buildBuffer(s,"attribute","aPosition",3,"v3"),centroid:this.buildBuffer(s,"attribute","aCentroid",3,"v3"),normal:this.buildBuffer(s,"attribute","aNormal",3,"v3"),ambient:this.buildBuffer(s,"attribute","aAmbient",4,"v4"),diffuse:this.buildBuffer(s,"attribute","aDiffuse",4,"v4")},s.uniforms={resolution:this.buildBuffer(s,"uniform","uResolution",3,"3f",1),lightPosition:this.buildBuffer(s,"uniform","uLightPosition",3,"3fv",t),lightAmbient:this.buildBuffer(s,"uniform","uLightAmbient",4,"4fv",t),lightDiffuse:this.buildBuffer(s,"uniform","uLightDiffuse",4,"4fv",t)},this.program=s,this.gl.useProgram(this.program),s}}},FSS.WebGLRenderer.prototype.buildShader=function(t,e){if(!this.unsupported){var i=this.gl.createShader(t);return this.gl.shaderSource(i,e),this.gl.compileShader(i),this.gl.getShaderParameter(i,this.gl.COMPILE_STATUS)?i:(console.error(this.gl.getShaderInfoLog(i)),null)}},FSS.WebGLRenderer.prototype.buildBuffer=function(t,e,i,r,s,n){var o={buffer:this.gl.createBuffer(),size:r,structure:s,data:null};switch(e){case"attribute":o.location=this.gl.getAttribLocation(t,i);break;case"uniform":o.location=this.gl.getUniformLocation(t,i)}return n&&(o.data=new FSS.Array(n*r)),o},FSS.WebGLRenderer.VS=function(t){var e=["precision mediump float;","#define LIGHTS "+t,"attribute float aSide;","attribute vec3 aPosition;","attribute vec3 aCentroid;","attribute vec3 aNormal;","attribute vec4 aAmbient;","attribute vec4 aDiffuse;","uniform vec3 uResolution;","uniform vec3 uLightPosition[LIGHTS];","uniform vec4 uLightAmbient[LIGHTS];","uniform vec4 uLightDiffuse[LIGHTS];","varying vec4 vColor;","void main() {","vColor = vec4(0.0);","vec3 position = aPosition / uResolution * 2.0;","for (int i = 0; i < LIGHTS; i++) {","vec3 lightPosition = uLightPosition[i];","vec4 lightAmbient = uLightAmbient[i];","vec4 lightDiffuse = uLightDiffuse[i];","vec3 ray = normalize(lightPosition - aCentroid);","float illuminance = dot(aNormal, ray);","if (aSide == 0.0) {","illuminance = max(illuminance, 0.0);","} else if (aSide == 1.0) {","illuminance = abs(min(illuminance, 0.0));","} else if (aSide == 2.0) {","illuminance = max(abs(illuminance), 0.0);","}","vColor += aAmbient * lightAmbient;","vColor += aDiffuse * lightDiffuse * illuminance;","}","vColor = clamp(vColor, 0.0, 1.0);","gl_Position = vec4(position, 1.0);","}"].join("\n");return e},FSS.WebGLRenderer.FS=function(){var t=["precision mediump float;","varying vec4 vColor;","void main() {","gl_FragColor = vColor;","}"].join("\n");return t},FSS.SVGRenderer=function(){FSS.Renderer.call(this),this.element=document.createElementNS(FSS.SVGNS,"svg"),this.element.setAttribute("xmlns",FSS.SVGNS),this.element.setAttribute("version","1.1"),this.element.style.display="block",this.setSize(300,150)},FSS.SVGRenderer.prototype=Object.create(FSS.Renderer.prototype),FSS.SVGRenderer.prototype.setSize=function(t,e){return FSS.Renderer.prototype.setSize.call(this,t,e),this.element.setAttribute("width",t),this.element.setAttribute("height",e),this},FSS.SVGRenderer.prototype.clear=function(){FSS.Renderer.prototype.clear.call(this);for(var t=this.element.childNodes.length-1;t>=0;t--)this.element.removeChild(this.element.childNodes[t]);return this},FSS.SVGRenderer.prototype.render=function(t){FSS.Renderer.prototype.render.call(this,t);var e,i,r,s,n,o;for(e=t.meshes.length-1;e>=0;e--)if(i=t.meshes[e],i.visible)for(i.update(t.lights,!0),r=i.geometry.triangles.length-1;r>=0;r--)s=i.geometry.triangles[r],s.polygon.parentNode!==this.element&&this.element.appendChild(s.polygon),n=this.formatPoint(s.a)+" ",n+=this.formatPoint(s.b)+" ",n+=this.formatPoint(s.c),o=this.formatStyle(s.color.format()),s.polygon.setAttributeNS(null,"points",n),s.polygon.setAttributeNS(null,"style",o);return this},FSS.SVGRenderer.prototype.formatPoint=function(t){return this.halfWidth+t.position[0]+","+(this.halfHeight-t.position[1])},FSS.SVGRenderer.prototype.formatStyle=function(t){var e="fill:"+t+";";return e+="stroke:"+t+";"}; --------------------------------------------------------------------------------