├── img
└── numbers.ai
├── wip-screenshots
├── webgl-001.png
├── webgl-002.gif
├── webgl-003.png
├── webgl-004.gif
├── webgl-005.gif
├── webgl-006.gif
├── webgl-006.png
├── webgl-007.png
├── webgl-008.png
├── webgl-009.gif
├── webgl-010.gif
├── webgl-011.png
└── webgl-012.gif
├── index.html
├── README.md
├── LICENSE
└── ready.js
/img/numbers.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carloscabo/a-million-times-webgl-demo/HEAD/img/numbers.ai
--------------------------------------------------------------------------------
/wip-screenshots/webgl-001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carloscabo/a-million-times-webgl-demo/HEAD/wip-screenshots/webgl-001.png
--------------------------------------------------------------------------------
/wip-screenshots/webgl-002.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carloscabo/a-million-times-webgl-demo/HEAD/wip-screenshots/webgl-002.gif
--------------------------------------------------------------------------------
/wip-screenshots/webgl-003.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carloscabo/a-million-times-webgl-demo/HEAD/wip-screenshots/webgl-003.png
--------------------------------------------------------------------------------
/wip-screenshots/webgl-004.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carloscabo/a-million-times-webgl-demo/HEAD/wip-screenshots/webgl-004.gif
--------------------------------------------------------------------------------
/wip-screenshots/webgl-005.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carloscabo/a-million-times-webgl-demo/HEAD/wip-screenshots/webgl-005.gif
--------------------------------------------------------------------------------
/wip-screenshots/webgl-006.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carloscabo/a-million-times-webgl-demo/HEAD/wip-screenshots/webgl-006.gif
--------------------------------------------------------------------------------
/wip-screenshots/webgl-006.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carloscabo/a-million-times-webgl-demo/HEAD/wip-screenshots/webgl-006.png
--------------------------------------------------------------------------------
/wip-screenshots/webgl-007.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carloscabo/a-million-times-webgl-demo/HEAD/wip-screenshots/webgl-007.png
--------------------------------------------------------------------------------
/wip-screenshots/webgl-008.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carloscabo/a-million-times-webgl-demo/HEAD/wip-screenshots/webgl-008.png
--------------------------------------------------------------------------------
/wip-screenshots/webgl-009.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carloscabo/a-million-times-webgl-demo/HEAD/wip-screenshots/webgl-009.gif
--------------------------------------------------------------------------------
/wip-screenshots/webgl-010.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carloscabo/a-million-times-webgl-demo/HEAD/wip-screenshots/webgl-010.gif
--------------------------------------------------------------------------------
/wip-screenshots/webgl-011.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carloscabo/a-million-times-webgl-demo/HEAD/wip-screenshots/webgl-011.png
--------------------------------------------------------------------------------
/wip-screenshots/webgl-012.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carloscabo/a-million-times-webgl-demo/HEAD/wip-screenshots/webgl-012.gif
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Document
6 |
7 |
8 |
9 |
10 |
11 |
12 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # a-million-times-webgl-demo
2 |
3 | See it working here [https://a-million-times.netlify.app/](https://a-million-times.netlify.app/)
4 |
5 | 
6 |
7 | ## Requires
8 |
9 | - `three.js` r75
10 | - `TweenJS`
11 |
12 | ## Disclaimer
13 | Yep, it seems 2D instead 3D, I'll try with something more 3Dish next time ;)
14 |
15 | This is only a proof of concept, expect fast-and-dirty code and problably some bugs / glitches.
16 | Enjoy ;)
17 |
18 | ## Original idea
19 |
20 | Fast WebGL test concept inspired by the **"A million Times"** kinetic installation by _Humans since 1982_
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Carlos Cabo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, 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,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/ready.js:
--------------------------------------------------------------------------------
1 | var
2 | scene,
3 | camera,
4 | renderer,
5 | materials = {},
6 | digits = [],
7 | π = Math.PI,
8 | delay_between_cols = 250,
9 | clock_anim_duration = 2500,
10 | timeout_between_animations = 7500,
11 | rotation = 0;
12 |
13 | $(document).ready(function(){
14 | //La magia aquí
15 | scene = new THREE.Scene();
16 |
17 | renderer = new THREE.WebGLRenderer({
18 | antialias:true
19 | });
20 | renderer.setSize( window.innerWidth, window.innerHeight );
21 | renderer.setClearColor( new THREE.Color( 0x323232 ), 1);
22 | document.body.appendChild( renderer.domElement );
23 |
24 | materials.grey = new THREE.MeshBasicMaterial({
25 | color: 0x666666,
26 | side: THREE.DoubleSide
27 | });
28 | materials.white = new THREE.MeshBasicMaterial({
29 | color: 0xffffff,
30 | side: THREE.DoubleSide
31 | });
32 | materials.red = new THREE.MeshBasicMaterial({
33 | color: 0x990000,
34 | side: THREE.DoubleSide
35 | });
36 |
37 | // var geometry = new THREE.BoxGeometry( 1, 1, 1 );
38 | // cube = new THREE.Mesh( geometry, material );
39 | // scene.add( cube );
40 |
41 | var
42 | digits_num = 4,
43 | rows = 5,
44 | cols = 3,
45 | radius = 1,
46 | stroke = 0.1,
47 | xy_offset = ( radius * 2 ) + ( stroke * 2 );
48 |
49 | for (var d = 0; d < digits_num; d++) {
50 | var digit = [];
51 | for (var i = 0; i < rows; i++) {
52 | var row = [];
53 | for (var j = 0; j < cols; j++) {
54 | row.push( new Clock3D( radius, stroke ) );
55 | row[ row.length -1 ].position.x = ( d * xy_offset * cols ) + ( j * xy_offset );
56 | row[ row.length -1 ].position.y = i * xy_offset * -1;
57 | }
58 | digit.push( row );
59 | }
60 | digits.push( digit );
61 | }
62 |
63 | var clock_w = (xy_offset * cols * digits_num) + xy_offset;
64 | var fov = 5;
65 | // Adjust camera to fit clock in screen width
66 | var dist = clock_w / 2 / (window.innerWidth / window.innerHeight) / Math.tan(Math.PI * fov / 360);
67 | console.log( dist );
68 | camera = new THREE.PerspectiveCamera( fov, window.innerWidth / window.innerHeight, 0.1, 1000 );
69 | camera.position.x = xy_offset * cols * digits_num / 2 - xy_offset / 2;
70 | camera.position.y = ( (rows - 1) * xy_offset ) / 2 * -1;
71 | camera.position.z = dist;
72 |
73 | // Preprocess chars from human readeable to radians
74 | convertCharsToRad();
75 |
76 | // Initial clock setting
77 | updateSeconds();
78 | setCurrentTimeAnalog();
79 |
80 | setInterval( function() {
81 | updateSeconds();
82 | }, 500 );
83 |
84 | render();
85 | // renderer.render( scene, camera );
86 |
87 | });
88 |
89 | // Scene render loop
90 | function render() {
91 | requestAnimationFrame( render );
92 | renderer.render( scene, camera );
93 | }
94 |
95 | // Clock 3D Object constructor
96 | function Clock3D ( rad, stk ) {
97 |
98 | var
99 | g, // geometry
100 | clock = new THREE.Object3D();
101 |
102 | // Clock sphere
103 | g = new THREE.RingGeometry( rad, rad + (stk/2), 64 );
104 | sp = new THREE.Mesh( g, materials.grey );
105 | clock.add( sp );
106 |
107 | // sec
108 | g = new THREE.PlaneGeometry( rad - (stk*1), 0.025, 1 );
109 | g.translate( (rad - (stk*1)) / 2, 0, -0.1 );
110 | sec = new THREE.Mesh( g, materials.red );
111 | sec.geometry.dynamic = true;
112 | sec.rotateZ( π / 2 );
113 | clock.add( sec );
114 |
115 | // Min
116 | g = new THREE.PlaneGeometry( rad - (stk*1.5), stk, 1 );
117 | g.translate( (rad - (stk*1.5)) / 2, 0, 0 );
118 | min = new THREE.Mesh( g, materials.white );
119 | min.rotateZ( π / 2 );
120 | clock.add( min );
121 |
122 | // hour
123 | g = new THREE.PlaneGeometry( rad - (stk*1.5), stk, 1 );
124 | g.translate( (rad - (stk*1.5)) / 2, 0, 0 );
125 | hour = new THREE.Mesh( g, materials.white );
126 | hour.rotateZ( π / 2 );
127 | clock.add( hour );
128 |
129 | // Clock axis
130 | g = new THREE.CircleGeometry( stk/2, 32 );
131 | g.translate( 0, 0, 0.1 );
132 | ax = new THREE.Mesh( g, materials.white );
133 | clock.add( ax );
134 |
135 | scene.add( clock );
136 | return clock;
137 | }
138 |
139 | // Updates clock's seconds to current time
140 | function updateSeconds() {
141 | var
142 | d = new Date(),
143 | s = d.getSeconds();
144 | // console.log(s);
145 | for (var d = 0; d < digits.length; d++) {
146 | for (var i = 0; i < digits[d].length; i++) {
147 | for (var j = 0; j < digits[d][i].length; j++) {
148 | digits[d][i][j].children[1].rotation.z = timeToRad ( s, 60 );
149 | }
150 | }
151 | }
152 | }
153 |
154 | // Updates clock's matrix to current time in digital format
155 | function setCurrentTimeDigital() {
156 | var
157 | current = getCurrentHourCharArray();
158 | for (var d = 0; d < digits.length; d++) {
159 | for (var i = 0; i < digits[d].length; i++) {
160 | for (var j = 0; j < digits[d][i].length; j++) {
161 | // digits[d][i][j].children[2].rotation.z = chars[ current[d] ][i][j][0];
162 | // digits[d][i][j].children[3].rotation.z = chars[ current[d] ][i][j][1];
163 | var clock_delay = 1000 + ( ( d * (digits[d].length - 1) + j - i ) * delay_between_cols );
164 | createjs.Tween.get( digits[d][i][j].children[2].rotation ).wait( clock_delay ).to({ z: chars[ current[d+''] ][i][j][0] }, clock_anim_duration, createjs.Ease.quintInOut);
165 | createjs.Tween.get( digits[d][i][j].children[3].rotation ).wait( clock_delay ).to({ z: chars[ current[d+''] ][i][j][1] }, clock_anim_duration, createjs.Ease.quintInOut);
166 | }
167 | }
168 | }
169 | setTimeout( function(){
170 | setCurrentTimeAnalog();
171 | }, timeout_between_animations );
172 | }
173 |
174 | // Updates clock's matrix to current time in analog format
175 | function setCurrentTimeAnalog() {
176 | var
177 | d = new Date(),
178 | m = d.getMinutes();
179 | h = d.getHours() % 12 || 0;
180 | for (var d = 0; d < digits.length; d++) {
181 | for (var i = 0; i < digits[d].length; i++) {
182 | for (var j = 0; j < digits[d][i].length; j++) {
183 |
184 | var clock_delay = 1000 + ( ( d * (digits[d].length - 1) + j - i ) * delay_between_cols );
185 |
186 | createjs.Tween.get( digits[d][i][j].children[2].rotation ).wait( clock_delay ).to({ z: timeToRad ( m, 60 ) }, clock_anim_duration, createjs.Ease.quintInOut);
187 | createjs.Tween.get( digits[d][i][j].children[3].rotation ).wait( clock_delay ).to({ z: timeToRad ( h, 12 ) }, clock_anim_duration, createjs.Ease.quintInOut);
188 | // Min
189 | //digits[d][i][j].children[2].rotation.z = timeToRad ( m, 60 );
190 | // Hour
191 | //digits[d][i][j].children[3].rotation.z = timeToRad ( h, 12 );
192 | }
193 | }
194 | }
195 | setTimeout( function(){
196 | setCurrentTimeDigital();
197 | }, timeout_between_animations );
198 | }
199 |
200 | // Converts a time value to its radian equivalency
201 | function timeToRad ( value, base ) {
202 | return ( value / base ) * 2 * π * -1 + ( π / 2);
203 | }
204 |
205 | // Converts current time to an array of chars
206 | // 13:45 -> ['1','3','4','5']
207 | function getCurrentHourCharArray () {
208 | var
209 | d = new Date(),
210 | h = d.getHours(),
211 | m = d.getMinutes();
212 | return ('' + zeroPad(h) + zeroPad(m) ).split('');
213 | }
214 |
215 | // Pad an string with zeros
216 | function zeroPad(i) {
217 | return (i < 10) ? '0' + i : i ;
218 | }
219 |
220 | // Pre-process the char values below to radians, much easier to rotate
221 | // the clock's hands
222 | function convertCharsToRad () {
223 | $.each(chars, function(index, char) {
224 | for (var i = 0; i < char.length; i++) {
225 | for (var j = 0; j < char[i].length; j++) {
226 | char[i][j][0] = timeToRad ( char[i][j][0], 12 );
227 | char[i][j][1] = timeToRad ( char[i][j][1], 12 );
228 | }
229 | }
230 | });
231 | }
232 |
233 | // The digital characters are defined using the hour positions of the
234 | // clocks hands 0 - 12
235 | var chars = {
236 | '0': [
237 | [ [3,6], [3,9], [6,9] ],
238 | [ [0,6], [6,6], [0,6] ],
239 | [ [0,6], [0,6], [0,6] ],
240 | [ [0,6], [0,0], [0,6] ],
241 | [ [0,3], [3,9], [0,9] ]
242 | ],
243 | '1': [
244 | [ [3,6], [3,9], [6,9] ],
245 | [ [0,3], [6,9], [0,6] ],
246 | [ [7.5,7.5], [0,6], [0,6] ],
247 | [ [7.5,7.5], [0,6], [0,6] ],
248 | [ [7.5,7.5], [0,3], [0,9] ]
249 | ],
250 | '2': [
251 | [ [3,6], [3,9], [6,9] ],
252 | [ [0,3], [6,9], [0,6] ],
253 | [ [3,6], [0,9], [0,9] ],
254 | [ [0,6], [0,3], [6,9] ],
255 | [ [0,3], [3,9], [0,9] ]
256 | ],
257 | '3': [
258 | [ [3,6], [3,9], [6,9] ],
259 | [ [0,3], [6,9], [0,6] ],
260 | [ [3,3], [6,9], [0,6] ],
261 | [ [3,6], [0,9], [0,6] ],
262 | [ [0,3], [3,9], [0,9] ]
263 | ],
264 | '4': [
265 | [ [3,6], [6,9], [6,9] ],
266 | [ [0,6], [0,6], [0,6] ],
267 | [ [0,6], [0,3], [0,6] ],
268 | [ [0,3], [6,9], [0,6] ],
269 | [ [7.5,7.5], [0,3], [0,9] ]
270 | ],
271 | '5': [
272 | [ [3,6], [3,9], [6,9] ],
273 | [ [0,6], [3,6], [0,9] ],
274 | [ [0,3], [0,3], [6,9] ],
275 | [ [3,6], [0,9], [0,6] ],
276 | [ [0,3], [3,9], [0,9] ]
277 | ],
278 | '6': [
279 | [ [3,6], [3,9], [6,9] ],
280 | [ [0,6], [3,6], [0,9] ],
281 | [ [0,6], [0,3], [6,9] ],
282 | [ [0,6], [0,6], [0,6] ],
283 | [ [0,3], [3,9], [0,9] ]
284 | ],
285 | '7': [
286 | [ [3,6], [3,9], [6,9] ],
287 | [ [0,3], [6,9], [0,6] ],
288 | [ [7.5,7.5], [6,9], [0,6] ],
289 | [ [7.5,7.5], [0,6], [0,6] ],
290 | [ [7.5,7.5], [0,3], [0,9] ]
291 | ],
292 | '8': [
293 | [ [3,6], [3,9], [6,9] ],
294 | [ [0,6], [6,9], [0,6] ],
295 | [ [0,6], [6,9], [0,6] ],
296 | [ [0,6], [0,0], [0,6] ],
297 | [ [0,3], [3,9], [0,9] ]
298 | ],
299 | '9': [
300 | [ [3,6], [3,9], [6,9] ],
301 | [ [0,6], [6,9], [0,6] ],
302 | [ [0,3], [6,9], [0,6] ],
303 | [ [7.5,7.5], [0,6], [0,6] ],
304 | [ [7.5,7.5], [0,3], [0,9] ]
305 | ],
306 | 'L': [
307 | [ [3,6], [6,9], [1.5,1.5] ],
308 | [ [0,6], [0,6], [1.5,1.5] ],
309 | [ [0,6], [0,6], [1.5,1.5] ],
310 | [ [0,6], [0,3], [6,9] ],
311 | [ [0,3], [3,9], [0,9] ]
312 | ],
313 | 'V': [
314 | [ [3,6], [3,6], [6,9] ],
315 | [ [0,6], [0,6], [0,6] ],
316 | [ [0,6], [0,6], [0,6] ],
317 | [ [0,4.5], [0,0], [0,7.5] ],
318 | [ [7.5,7.5], [1.5,10.5], [7.5,7.5] ]
319 | ],
320 | 'E': [
321 | [ [3,6], [3,9], [6,9] ],
322 | [ [0,6], [3,6], [0,9] ],
323 | [ [0,6], [3,6], [9,9] ],
324 | [ [0,6], [0,3], [6,9] ],
325 | [ [0,3], [3,9], [0,9] ]
326 | ],
327 | };
328 |
--------------------------------------------------------------------------------