Recommendation
More Effects...
JS
1
var Fireworks = function(){
2
    /*=============================================================================*/   
3
    /* Utility
4
    /*=============================================================================*/
5
    var self = this;
6
    var rand = function(rMi, rMa){return ~~((Math.random()*(rMa-rMi+1))+rMi);}
7
    var hitTest = function(x1, y1, w1, h1, x2, y2, w2, h2){return !(x1 + w1 < x2 || x2 + w2 < x1 || y1 + h1 < y2 || y2 + h2 < y1);};
8
    window.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){window.setTimeout(a,1E3/60)}}();
9
    
10
    /*=============================================================================*/   
11
    /* Initialize
12
    /*=============================================================================*/
13
    self.init = function(){ 
14
    self.dt = 0;
15
        self.oldTime = Date.now();
16
        self.canvas = document.createElement('canvas');             
17
        self.canvasContainer = $('#canvas-container');
18
        
19
        var canvasContainerDisabled = document.getElementById('canvas-container');
20
        self.canvas.onselectstart = function() {
21
            return false;
22
        };
23
        
24
        self.canvas.width = self.cw = 600;
25
        self.canvas.height = self.ch = 400; 
26
        
27
        self.particles = [];    
28
        self.partCount = 30;
29
        self.fireworks = [];    
30
        self.mx = self.cw/2;
31
        self.my = self.ch/2;
32
        self.currentHue = 170;
33
        self.partSpeed = 5;
34
        self.partSpeedVariance = 10;
35
        self.partWind = 50;
36
        self.partFriction = 5;
37
        self.partGravity = 1;
38
        self.hueMin = 150;
39
        self.hueMax = 200;
40
        self.fworkSpeed = 2;
41
        self.fworkAccel = 4;
42
        self.hueVariance = 30;
43
        self.flickerDensity = 20;
44
        self.showShockwave = false;
45
        self.showTarget = true;
46
        self.clearAlpha = 25;
47
 
48
        self.canvasContainer.append(self.canvas);
49
        self.ctx = self.canvas.getContext('2d');
50
        self.ctx.lineCap = 'round';
51
        self.ctx.lineJoin = 'round';
52
        self.lineWidth = 1;
53
        self.bindEvents();          
54
        self.canvasLoop();
55
        
56
        self.canvas.onselectstart = function() {
57
            return false;
58
        };
59
    
60
        
61
    };      
62
    
63
    /*=============================================================================*/   
64
    /* Particle Constructor
65
    /*=============================================================================*/
66
    var Particle = function(x, y, hue){
67
        this.x = x;
68
        this.y = y;
69
        this.coordLast = [
70
            {x: x, y: y},
71
            {x: x, y: y},
72
            {x: x, y: y}
73
        ];
74
        this.angle = rand(0, 360);
75
        this.speed = rand(((self.partSpeed - self.partSpeedVariance) <= 0) ? 1 : self.partSpeed - self.partSpeedVariance, (self.partSpeed + self.partSpeedVariance));
76
        this.friction = 1 - self.partFriction/100;
77
        this.gravity = self.partGravity/2;
78
        this.hue = rand(hue-self.hueVariance, hue+self.hueVariance);
79
        this.brightness = rand(50, 80);
80
        this.alpha = rand(40,100)/100;
81
        this.decay = rand(10, 50)/1000;
82
        this.wind = (rand(0, self.partWind) - (self.partWind/2))/25;
83
        this.lineWidth = self.lineWidth;
84
    };
85
    
86
    Particle.prototype.update = function(index){
87
        var radians = this.angle * Math.PI / 180;
88
        var vx = Math.cos(radians) * this.speed;
89
        var vy = Math.sin(radians) * this.speed + this.gravity;
90
        this.speed *= this.friction;
91
                        
92
        this.coordLast[2].x = this.coordLast[1].x;
93
        this.coordLast[2].y = this.coordLast[1].y;
94
        this.coordLast[1].x = this.coordLast[0].x;
95
        this.coordLast[1].y = this.coordLast[0].y;
96
        this.coordLast[0].x = this.x;
97
        this.coordLast[0].y = this.y;
98
        
99
        this.x += vx * self.dt;
100
        this.y += vy * self.dt;
101
        
102
        this.angle += this.wind;                
103
        this.alpha -= this.decay;
104
        
105
        if(!hitTest(0,0,self.cw,self.ch,this.x-this.radius, this.y-this.radius, this.radius*2, this.radius*2) || this.alpha < .05){                 
106
            self.particles.splice(index, 1);    
107
        }           
108
    };
109
    
110
    Particle.prototype.draw = function(){
111
        var coordRand = (rand(1,3)-1);
112
        self.ctx.beginPath();                               
113
        self.ctx.moveTo(Math.round(this.coordLast[coordRand].x), Math.round(this.coordLast[coordRand].y));
114
        self.ctx.lineTo(Math.round(this.x), Math.round(this.y));
115
        self.ctx.closePath();               
116
        self.ctx.strokeStyle = 'hsla('+this.hue+', 100%, '+this.brightness+'%, '+this.alpha+')';
117
        self.ctx.stroke();              
118
        
119
        if(self.flickerDensity > 0){
120
            var inverseDensity = 50 - self.flickerDensity;                  
121
            if(rand(0, inverseDensity) === inverseDensity){
122
                self.ctx.beginPath();
123
                self.ctx.arc(Math.round(this.x), Math.round(this.y), rand(this.lineWidth,this.lineWidth+3)/2, 0, Math.PI*2, false)
124
                self.ctx.closePath();
125
                var randAlpha = rand(50,100)/100;
126
                self.ctx.fillStyle = 'hsla('+this.hue+', 100%, '+this.brightness+'%, '+randAlpha+')';
127
                self.ctx.fill();
128
            }   
129
        }
130
    };
131
    
132
    /*=============================================================================*/   
133
    /* Create Particles
134
    /*=============================================================================*/
135
    self.createParticles = function(x,y, hue){
136
        var countdown = self.partCount;
137
        while(countdown--){                     
138
            self.particles.push(new Particle(x, y, hue));
139
        }
140
    };
141
    
142
    /*=============================================================================*/   
143
    /* Update Particles
144
    /*=============================================================================*/       
145
    self.updateParticles = function(){
146
        var i = self.particles.length;
147
        while(i--){
148
            var p = self.particles[i];
149
            p.update(i);
150
        };
151
    };
152
    
153
    /*=============================================================================*/   
154
    /* Draw Particles
155
    /*=============================================================================*/
156
    self.drawParticles = function(){
157
        var i = self.particles.length;
158
        while(i--){
159
            var p = self.particles[i];              
160
            p.draw();               
161
        };
162
    };
163
    
164
    /*=============================================================================*/   
165
    /* Firework Constructor
166
    /*=============================================================================*/
167
    var Firework = function(startX, startY, targetX, targetY){
168
        this.x = startX;
169
        this.y = startY;
170
        this.startX = startX;
171
        this.startY = startY;
172
        this.hitX = false;
173
        this.hitY = false;
174
        this.coordLast = [
175
            {x: startX, y: startY},
176
            {x: startX, y: startY},
177
            {x: startX, y: startY}
178
        ];
179
        this.targetX = targetX;
180
        this.targetY = targetY;
181
        this.speed = self.fworkSpeed;
182
        this.angle = Math.atan2(targetY - startY, targetX - startX);
183
        this.shockwaveAngle = Math.atan2(targetY - startY, targetX - startX)+(90*(Math.PI/180));
184
        this.acceleration = self.fworkAccel/100;
185
        this.hue = self.currentHue;
186
        this.brightness = rand(50, 80);
187
        this.alpha = rand(50,100)/100;
188
        this.lineWidth = self.lineWidth;
189
        this.targetRadius = 1;
190
    };
191
    
192
    Firework.prototype.update = function(index){
193
        self.ctx.lineWidth = this.lineWidth;
194
            
195
        vx = Math.cos(this.angle) * this.speed,
196
        vy = Math.sin(this.angle) * this.speed;
197
        this.speed *= 1 + this.acceleration;                
198
        this.coordLast[2].x = this.coordLast[1].x;
199
        this.coordLast[2].y = this.coordLast[1].y;
200
        this.coordLast[1].x = this.coordLast[0].x;
201
        this.coordLast[1].y = this.coordLast[0].y;
202
        this.coordLast[0].x = this.x;
203
        this.coordLast[0].y = this.y;
204
        
205
        if(self.showTarget){
206
            if(this.targetRadius < 8){
207
                this.targetRadius += .25 * self.dt;
208
            } else {
209
                this.targetRadius = 1 * self.dt;    
210
            }
211
        }
212
        
213
        if(this.startX >= this.targetX){
214
            if(this.x + vx <= this.targetX){
215
                this.x = this.targetX;
216
                this.hitX = true;
217
            } else {
218
                this.x += vx * self.dt;
219
            }
220
        } else {
221
            if(this.x + vx >= this.targetX){
222
                this.x = this.targetX;
223
                this.hitX = true;
224
            } else {
225
                this.x += vx * self.dt;
226
            }
227
        }
228
        
229
        if(this.startY >= this.targetY){
230
            if(this.y + vy <= this.targetY){
231
                this.y = this.targetY;
232
                this.hitY = true;
233
            } else {
234
                this.y += vy * self.dt;
235
            }
236
        } else {
237
            if(this.y + vy >= this.targetY){
238
                this.y = this.targetY;
239
                this.hitY = true;
240
            } else {
241
                this.y += vy * self.dt;
242
            }
243
        }               
244
        
245
        if(this.hitX && this.hitY){
246
            var randExplosion = rand(0, 9);
247
            self.createParticles(this.targetX, this.targetY, this.hue);
248
            self.fireworks.splice(index, 1);                    
249
        }
250
    };
251
    
252
    Firework.prototype.draw = function(){
253
        self.ctx.lineWidth = this.lineWidth;
254
            
255
        var coordRand = (rand(1,3)-1);                  
256
        self.ctx.beginPath();                           
257
        self.ctx.moveTo(Math.round(this.coordLast[coordRand].x), Math.round(this.coordLast[coordRand].y));
258
        self.ctx.lineTo(Math.round(this.x), Math.round(this.y));
259
        self.ctx.closePath();
260
        self.ctx.strokeStyle = 'hsla('+this.hue+', 100%, '+this.brightness+'%, '+this.alpha+')';
261
        self.ctx.stroke();  
262
        
263
        if(self.showTarget){
264
            self.ctx.save();
265
            self.ctx.beginPath();
266
            self.ctx.arc(Math.round(this.targetX), Math.round(this.targetY), this.targetRadius, 0, Math.PI*2, false)
267
            self.ctx.closePath();
268
            self.ctx.lineWidth = 1;
269
            self.ctx.stroke();
270
            self.ctx.restore();
271
        }
272
            
273
        if(self.showShockwave){
274
            self.ctx.save();
275
            self.ctx.translate(Math.round(this.x), Math.round(this.y));
276
            self.ctx.rotate(this.shockwaveAngle);
277
            self.ctx.beginPath();
278
            self.ctx.arc(0, 0, 1*(this.speed/5), 0, Math.PI, true);
279
            self.ctx.strokeStyle = 'hsla('+this.hue+', 100%, '+this.brightness+'%, '+rand(25, 60)/100+')';
280
            self.ctx.lineWidth = this.lineWidth;
281
            self.ctx.stroke();
282
            self.ctx.restore();
283
        }                                
284
    };
285
    
286
    /*=============================================================================*/   
287
    /* Create Fireworks
288
    /*=============================================================================*/
289
    self.createFireworks = function(startX, startY, targetX, targetY){          
290
        self.fireworks.push(new Firework(startX, startY, targetX, targetY));
291
    };
292
    
293
    /*=============================================================================*/   
294
    /* Update Fireworks
295
    /*=============================================================================*/       
296
    self.updateFireworks = function(){
297
        var i = self.fireworks.length;
298
        while(i--){
299
            var f = self.fireworks[i];
300
            f.update(i);
301
        };
302
    };
303
    
304
    /*=============================================================================*/   
305
    /* Draw Fireworks
306
    /*=============================================================================*/
307
    self.drawFireworks = function(){
308
        var i = self.fireworks.length;          
309
        while(i--){
310
            var f = self.fireworks[i];      
311
            f.draw();
312
        };
313
    };
314
    
315
    /*=============================================================================*/   
316
    /* Events
317
    /*=============================================================================*/
318
    self.bindEvents = function(){
319
        $(window).on('resize', function(){          
320
            clearTimeout(self.timeout);
321
            self.timeout = setTimeout(function() {
322
                self.ctx.lineCap = 'round';
323
                self.ctx.lineJoin = 'round';
324
            }, 100);
325
        });
326
        
327
        $(self.canvas).on('mousedown', function(e){
328
            var randLaunch = rand(0, 5);
329
            self.mx = e.pageX - self.canvasContainer.offset().left;
330
            self.my = e.pageY - self.canvasContainer.offset().top;
331
            self.currentHue = rand(self.hueMin, self.hueMax);
332
            self.createFireworks(self.cw/2, self.ch, self.mx, self.my); 
333
            
334
            $(self.canvas).on('mousemove.fireworks', function(e){
335
                var randLaunch = rand(0, 5);
336
                self.mx = e.pageX - self.canvasContainer.offset().left;
337
                self.my = e.pageY - self.canvasContainer.offset().top;
338
                self.currentHue = rand(self.hueMin, self.hueMax);
339
                self.createFireworks(self.cw/2, self.ch, self.mx, self.my);                                 
340
            }); 
341
            
342
        });
343
        
344
        $(self.canvas).on('mouseup', function(e){
345
            $(self.canvas).off('mousemove.fireworks');                                  
346
        });
347
                    
348
    }
349
    
350
    /*=============================================================================*/   
351
    /* Clear Canvas
352
    /*=============================================================================*/
353
    self.clear = function(){
354
        self.particles = [];
355
        self.fireworks = [];
356
        self.ctx.clearRect(0, 0, self.cw, self.ch);
357
    };
358
  
359
  /*=============================================================================*/ 
360
    /* Delta
361
    /*=============================================================================*/
362
  self.updateDelta = function(){
363
        var newTime = Date.now();
364
        self.dt = (newTime - self.oldTime)/16;
365
        self.dt = (self.dt > 5) ? 5 : self.dt;
366
        self.oldTime = newTime; 
367
    }
368
    
369
    /*=============================================================================*/   
370
    /* Main Loop
371
    /*=============================================================================*/
372
    self.canvasLoop = function(){
373
        requestAnimFrame(self.canvasLoop, self.canvas);
374
    self.updateDelta();
375
        self.ctx.globalCompositeOperation = 'destination-out';
376
        self.ctx.fillStyle = 'rgba(0,0,0,'+self.clearAlpha/100+')';
377
        self.ctx.fillRect(0,0,self.cw,self.ch);
378
        self.ctx.globalCompositeOperation = 'lighter';
379
        self.updateFireworks();
380
        self.updateParticles();
381
        self.drawFireworks();           
382
        self.drawParticles();           
383
    };
384
    
385
    self.init();
386
  
387
  var initialLaunchCount = 10;
388
  while(initialLaunchCount--){
389
    setTimeout(function(){
390
        self.fireworks.push(new Firework(self.cw/2, self.ch, rand(50, self.cw-50), rand(50, self.ch/2)-50));
391
    }, initialLaunchCount*200);
392
  }
393
    
394
}
395
 
396
/*=============================================================================*/   
397
/* GUI
398
/*=============================================================================*/   
399
var guiPresets = {
400
              "preset": "Default",
401
              "remembered": {
402
                "Default": {
403
                  "0": {
404
                    "fworkSpeed": 2,
405
                    "fworkAccel": 4,
406
                    "showShockwave": false,
407
                    "showTarget": true,
408
                    "partCount": 30,
409
                    "partSpeed": 5,
410
                    "partSpeedVariance": 10,
411
                    "partWind": 50,
412
                    "partFriction": 5,
413
                    "partGravity": 1,
414
                    "flickerDensity": 20,
415
                    "hueMin": 150,
416
                    "hueMax": 200,
417
                    "hueVariance": 30,
418
                    "lineWidth": 1,
419
                    "clearAlpha": 25
420
                  }
421
                },
422
                "Anti Gravity": {
423
                  "0": {
424
                    "fworkSpeed": 4,
425
                    "fworkAccel": 10,
426
                    "showShockwave": true,
427
                    "showTarget": false,
428
                    "partCount": 150,
429
                    "partSpeed": 5,
430
                    "partSpeedVariance": 10,
431
                    "partWind": 10,
432
                    "partFriction": 10,
433
                    "partGravity": -10,
434
                    "flickerDensity": 30,
435
                    "hueMin": 0,
436
                    "hueMax": 360,
437
                    "hueVariance": 30,
438
                    "lineWidth": 1,
439
                    "clearAlpha": 50
440
                  }
441
                },
442
                "Battle Field": {
443
                  "0": {
444
                    "fworkSpeed": 10,
445
                    "fworkAccel": 20,
446
                    "showShockwave": true,
447
                    "showTarget": true,
448
                    "partCount": 200,
449
                    "partSpeed": 30,
450
                    "partSpeedVariance": 5,
451
                    "partWind": 0,
452
                    "partFriction": 5,
453
                    "partGravity": 0,
454
                    "flickerDensity": 0,
455
                    "hueMin": 20,
456
                    "hueMax": 30,
457
                    "hueVariance": 10,
458
                    "lineWidth": 1,
459
                    "clearAlpha": 40
460
                  }
461
                },
462
                "Mega Blast": {
463
                  "0": {
464
                    "fworkSpeed": 3,
465
                    "fworkAccel": 3,
466
                    "showShockwave": true,
467
                    "showTarget": true,
468
                    "partCount": 500,
469
                    "partSpeed": 50,
470
                    "partSpeedVariance": 5,
471
                    "partWind": 0,
472
                    "partFriction": 0,
473
                    "partGravity": 0,
474
                    "flickerDensity": 0,
475
                    "hueMin": 0,
476
                    "hueMax": 360,
477
                    "hueVariance": 30,
478
                    "lineWidth": 20,
479
                    "clearAlpha": 20
480
                  }
481
                },
482
                "Nimble": {
483
                  "0": {
484
                    "fworkSpeed": 10,
485
                    "fworkAccel": 50,
486
                    "showShockwave": false,
487
                    "showTarget": false,
488
                    "partCount": 120,
489
                    "partSpeed": 10,
490
                    "partSpeedVariance": 10,
491
                    "partWind": 100,
492
                    "partFriction": 50,
493
                    "partGravity": 0,
494
                    "flickerDensity": 20,
495
                    "hueMin": 0,
496
                    "hueMax": 360,
497
                    "hueVariance": 30,
498
                    "lineWidth": 1,
499
                    "clearAlpha": 80
500
                  }
501
                },
502
                "Slow Launch": {
503
                  "0": {
504
                    "fworkSpeed": 2,
505
                    "fworkAccel": 2,
506
                    "showShockwave": false,
507
                    "showTarget": false,
508
                    "partCount": 200,
509
                    "partSpeed": 10,
510
                    "partSpeedVariance": 0,
511
                    "partWind": 100,
512
                    "partFriction": 0,
513
                    "partGravity": 2,
514
                    "flickerDensity": 50,
515
                    "hueMin": 0,
516
                    "hueMax": 360,
517
                    "hueVariance": 20,
518
                    "lineWidth": 4,
519
                    "clearAlpha": 10
520
                  }
521
                },
522
                "Perma Trail": {
523
                  "0": {
524
                    "fworkSpeed": 4,
525
                    "fworkAccel": 10,
526
                    "showShockwave": false,
527
                    "showTarget": false,
528
                    "partCount": 150,
529
                    "partSpeed": 10,
530
                    "partSpeedVariance": 10,
531
                    "partWind": 100,
532
                    "partFriction": 3,
533
                    "partGravity": 0,
534
                    "flickerDensity": 0,
535
                    "hueMin": 0,
536
                    "hueMax": 360,
537
                    "hueVariance": 20,
538
                    "lineWidth": 1,
539
                    "clearAlpha": 0
540
                  }
541
                }
542
              },
543
              "closed": true,
544
              "folders": {
545
                "Fireworks": {
546
                  "preset": "Default",
547
                  "closed": false,
548
                  "folders": {}
549
                },
550
                "Particles": {
551
                  "preset": "Default",
552
                  "closed": true,
553
                  "folders": {}
554
                },
555
                "Color": {
556
                  "preset": "Default",
557
                  "closed": true,
558
                  "folders": {}
559
                },
560
                "Other": {
561
                  "preset": "Default",
562
                  "closed": true,
563
                  "folders": {}
564
                }
565
              }
566
            };
567
 
568
 
569
var fworks = new Fireworks();
570
var gui = new dat.GUI({
571
    autoPlace: false,
572
    load: guiPresets,
573
    preset: 'Default'
574
});
575
var customContainer = document.getElementById('gui');
576
customContainer.appendChild(gui.domElement);
577
 
578
var guiFireworks = gui.addFolder('Fireworks');
579
guiFireworks.add(fworks, 'fworkSpeed').min(1).max(10).step(1);
580
guiFireworks.add(fworks, 'fworkAccel').min(0).max(50).step(1);
581
guiFireworks.add(fworks, 'showShockwave');
582
guiFireworks.add(fworks, 'showTarget');
583
 
584
var guiParticles = gui.addFolder('Particles');
585
guiParticles.add(fworks, 'partCount').min(0).max(500).step(1);  
586
guiParticles.add(fworks, 'partSpeed').min(1).max(100).step(1);
587
guiParticles.add(fworks, 'partSpeedVariance').min(0).max(50).step(1);
588
guiParticles.add(fworks, 'partWind').min(0).max(100).step(1);
589
guiParticles.add(fworks, 'partFriction').min(0).max(50).step(1);
590
guiParticles.add(fworks, 'partGravity').min(-20).max(20).step(1);
591
guiParticles.add(fworks, 'flickerDensity').min(0).max(50).step(1);
592
 
593
var guiColor = gui.addFolder('Color');
594
guiColor.add(fworks, 'hueMin').min(0).max(360).step(1);
595
guiColor.add(fworks, 'hueMax').min(0).max(360).step(1);
596
guiColor.add(fworks, 'hueVariance').min(0).max(180).step(1);
597
 
598
var guiOther = gui.addFolder('Other');
599
guiOther.add(fworks, 'lineWidth').min(1).max(20).step(1);
600
guiOther.add(fworks, 'clearAlpha').min(0).max(100).step(1);
601
guiOther.add(fworks, 'clear').name('Clear');
602
 
603
gui.remember(fworks);
 
CSS
1
html, body {
2
    margin: 0;  
3
    padding: 0;
4
}
5
 
6
body {
7
    background: #171717;
8
    color: #999;
9
    font: 100%/18px helvetica, arial, sans-serif;
10
}
11
 
12
a {
13
    color: #2fa1d6;
14
    font-weight: bold;
15
    text-decoration: none;
16
}
17
 
18
a:hover {
19
    color: #fff;    
20
}
21
 
22
#canvas-container {
23
    background: #000 url(http://jackrugile.com/lab/fireworks-v2/images/bg.jpg);
24
  height: 400px;
25
    left: 50%;
26
    margin: -200px 0 0 -300px;
27
    position: absolute;
28
    top: 50%;
29
  width: 600px;
30
    z-index: 2;
31
}
32
        
33
canvas {
34
    cursor: crosshair;
35
    display: block;
36
    position: relative;
37
    z-index: 3;
38
}
39
 
40
canvas:active {
41
    cursor: crosshair;
42
}
43
 
44
#skyline {
45
    background: url(http://jackrugile.com/lab/fireworks-v2/images/skyline.png) repeat-x 50% 0;
46
    bottom: 0;
47
    height: 135px;
48
    left: 0;
49
    position: absolute;
50
    width: 100%;
51
    z-index: 1; 
52
}
53
 
54
#mountains1 {
55
    background: url(http://jackrugile.com/lab/fireworks-v2/images/mountains1.png) repeat-x 40% 0;
56
    bottom: 0;
57
    height: 200px;
58
    left: 0;
59
    position: absolute;
60
    width: 100%;
61
    z-index: 1; 
62
}
63
 
64
#mountains2 {
65
    background: url(http://jackrugile.com/lab/fireworks-v2/images/mountains2.png) repeat-x 30% 0;
66
    bottom: 0;
67
    height: 250px;
68
    left: 0;
69
    position: absolute;
70
    width: 100%;
71
    z-index: 1; 
72
}
73
 
74
#gui {
75
    right: 0;
76
    position: fixed;
77
    top: 0;
78
    z-index: 3;
79
}
 
HTML
1
<div id="gui"></div>        
2
<div id="canvas-container">
3
  <div id="mountains2"></div>
4
  <div id="mountains1"></div>
5
  <div id="skyline"></div>
6
</div>
7
 
 

Join Effecthub.com

Working with Global Gaming Artists and Developers!


Or Sign Up with Your Email Address:
This field must contain a valid email
Password should be at least 1 character