Advertisement
Scroll to top
This post is part of a series called You Do The Math.
Simulate Projectile Motion with ActionScript 3.0
Playing Around with Elastic Collisions

During this tutorial we're going to tie math and design together. We'll explore Branden Hall and Joshua Davis' HYPE framework and create generative art from code.


Final Result Preview

Let's take a look at the final result we will be working towards:


Step 1: Project Structure

Before diving head-long into the code let's take a brief moment to familiarize ourselves with the project files.

Inside the source .zip file you will find a folder for each significant step, so that you can see exactly what changes have been made. Also, you will find a folder called Base, make a copy of this folder as this is will serve as our starting point.

Inside the Base folder we have a bin folder where our SWF will be published. A src folder which contains our ActionScript 3 and Flash Files and lastly a lib folder where we will store the HYPE framework's SWC files.

Overview of folder structureOverview of folder structureOverview of folder structure

Step 2: Getting HYPE

Next up, we need to grab the latest version of the HYPE framework from its home at www.hypeframework.org.

Once the download has completed, open the .zip file. You'll find two SWC files named hype-framework.swc and hype-extended.swc. Copy both of these to your Base\lib folder.

These SWC files are essentially a collection of source files for the framework, all rolled in to one file for ease.

Copying the required SWC filesCopying the required SWC filesCopying the required SWC files

Step 3: Adding SWCS to Flash

The final step needed to get everything up and running is to tell Flash to look for the two new SWC files when we compile the movie, otherwise it will throw a whole bunch of errors our way, not nice!

Open the Main.fla inside the Base\src folder, then choose File, Publish Settings. Select the Flash tab as shown, in the new window that opens select the Library Path tab.

Adding a SWC to FlashAdding a SWC to FlashAdding a SWC to Flash

Press the "Browse TO SWC" File button and proceed to add both SWC files to the Flash file. Once this is complete it's time to start adding some code!

Browsing for SWC filesBrowsing for SWC filesBrowsing for SWC files

Step 4: Init HYPE

Open up the Main.as source file in your chosen editor. Add the following private properties and the initHype() method:

1
2
public class Main extends MovieClip 
3
{
4
	// private properties

5
	private var bitmapCanvas:BitmapCanvas;
6
	private var clipContainer:Sprite;
7
	private var objectPool:ObjectPool;
8
	
9
	
10
	
11
	/**

12
	 * constructor

13
	 */
14
	public function Main()
15
	{
16
		// inits the Hype framework

17
		initHype();
18
	}
19
	
20
	
21
	
22
	/**

23
	 * initiation of the Hype members we will be using and configuration prior

24
	 * to running the animation

25
	 */
26
	private function initHype():void
27
	{
28
		// the clipContainer is used as a parent for all of our objects

29
		clipContainer = new Sprite();
30
		addChild(clipContainer);
31
	}
32
}

In the next few steps we'll be looking at each of these objects we've added as private properties, starting with the clipContainer Sprite.

As our design is going to have over a hundred objects all moving around the screen at once, we're going to need something to house them all - just adding them to the Stage will become problematic further down the line. The answer is to create a regular AS3 Sprite to act as a parent.


Step 5: The BitmapCanvas

The first real part of HYPE, the BitmapCanvas can be thought of as a Sprite or better still, a Bitmap/BitmapData to which we will be painting our objects, each frame, like a painters canvas.

We create it just below the clipContainer code and define it with a width and height to match the Stage. We add it to the Stage but also tell it to startCapture(clipContainer, true), this simply tells the BitmapCanvas to take a snapshot of the clipContainer each frame. For now though, keep this commented out!

1
2
	/**

3
	 * initiation of the Hype members we will be using and configuration prior

4
	 * to running the animation

5
	 */
6
	private function initHype():void
7
	{
8
		// the clipContainer is used as a parent for all of our objects

9
		clipContainer = new Sprite();
10
		addChild(clipContainer);
11
	
12
		// think of the BitmapCanvas as an empty space we will 'paint'

13
		// every frame with new image data

14
		bitmapCanvas = new BitmapCanvas(stage.stageWidth, stage.stageHeight);
15
		//bitmapCanvas.startCapture(clipContainer, true);

16
		addChild(bitmapCanvas);
17
	}
18
}

Step 6: The ObjectPool

If you have worked with games you've probably come across the concept of an ObjectPool. As creating new objects is too much of a hit on the processor we create a pool of a specified amount of objects before the game or application begins. We would then use the objects from this pool and upon running out we would recycle and reuse them all again, which avoids creating new objects. This is commonly used for bullets/lasers in games and the same logic is used in HYPE.

If you take a look at the Main.fla Library in Flash you will see I've created a MovieClip called circleShape and given it the Linkage Identifier of circleShape so we can create multiple copies of this object with code; this is what our ObjectPool will house.

a MovieClip to be used with ObjectPoola MovieClip to be used with ObjectPoola MovieClip to be used with ObjectPool

Add the ObjectPool below the BitmapCanvas code, like so:

1
2
	/**

3
	 * initiation of the Hype members we will be using and configuration prior

4
	 * to running the animation

5
	 */
6
	private function initHype():void
7
	{
8
		// the clipContainer is used as a parent for all of our objects

9
		clipContainer = new Sprite();
10
		addChild(clipContainer);
11
	
12
		// think of the BitmapCanvas as an empty space we will 'paint'

13
		// every frame with new image data

14
		bitmapCanvas = new BitmapCanvas(stage.stageWidth, stage.stageHeight);
15
		//bitmapCanvas.startCapture(clipContainer, true);

16
		addChild(bitmapCanvas);
17
		
18
		// create a collection of 10 objects and store them in an ObjectPool

19
		// for use in the animation

20
		objectPool = new ObjectPool(circleShape, 10);
21
	}

Step 7: Creating Objects Using ObjectPool.request();

Now we have our core players setup, the clipContainer, the BitmapCanvas and the ObjectPool with its 10 clips, it's time to start making things move.

To get an item from the ObjectPool we can use objectPool.request(); which will give us a circleShape MovieClip from the Flash Library to work with.

The ObjectPool also gives us the objectPool.onRequestObject() method which is a handy way to assign properties of a clip each time we request one. Add the following below where you instantiated the ObjectPool:

1
2
	// create a collection of 10 objects and store them in an ObjectPool

3
	// for use in the animation

4
	objectPool = new ObjectPool(circleShape, 10);
5
	
6
	// each time we request a new shape from the pool

7
	// it will perform the following

8
	objectPool.onRequestObject = function(clip:MovieClip):void
9
	{
10
		clip.x = Math.random() * stage.stageWidth;
11
		clip.y = Math.random() * stage.stageHeight;
12
		
13
		clipContainer.addChild(clip);
14
	}

Step 8: See the Result

With that new code added, every time we request an object from the pool by using objectPool.request() it will create a circleShape. Add it to the clipContainer and position it randomly on the screen. You can test this by amending the constructor to look like the following:

1
2
	/**

3
	 * constructor

4
	 */
5
	public function Main()
6
	{
7
		// inits the Hype framework

8
		initHype();
9
		
10
		objectPool.request();
11
	}

If all went well you should have a single, lonely circle on the screen.


Step 9: Requesting all Objects at Once

Do you remember we set the ObjectPool size to 10? Well we're going to up the ante and increase this to 100 objects by changing the following:

1
2
	// create a collection of 10 objects and store them in an ObjectPool

3
	// for use in the animation

4
	objectPool = new ObjectPool(circleShape, 100);

We can amend the earlier request statement to read as requestAll() like this:

1
2
	/**

3
	 * constructor

4
	 */
5
	public function Main()
6
	{
7
		// inits the Hype framework

8
		initHype();
9
		
10
		objectPool.requestAll();
11
	}

Now we should have 100 circles scattered over the screen's area.


Step 10: Adding FixedVibrations

Now we have our 100 circleShapes distributed around the screen it's time to make our design come to life by adding movement.

Let's start by applying a FixedVibration to the alpha and scale properties of each clip. We can use the ObjectPools onRequestObject method to implement it as shown:

1
2
	// create a collection of 10 objects and store them in an ObjectPool

3
	// for use in the animation

4
	objectPool = new ObjectPool(circleShape, 100);
5
	
6
	// each time we request a new shape from the pool

7
	// it will perform the following

8
	objectPool.onRequestObject = function(clip:MovieClip):void
9
	{
10
		clip.x = Math.random() * stage.stageWidth;
11
		clip.y = Math.random() * stage.stageHeight;
12
		
13
		// add a FixedVibration to the alpha and scale properties of each circleShape when requested

14
		var alphaVib:FixedVibration = new FixedVibration(clip, "alpha", 0.9, 0.05, 0.5, 1.5, false);
15
		var scaleVib:FixedVibration = new FixedVibration(clip, "scale", 0.9, 0.05, 0.05, 0.8, false);
16
		alphaVib.start();
17
		scaleVib.start();
18
		
19
		clipContainer.addChild(clip);
20
	}

Let's have a closer look at the FixedVibration objects we created. Each FixedVibration object we create takes 7 parameters, respectively they are as follows:

  • The object to apply the FixedVibration to, in our case our circleShape called "clip".
  • The property to apply the FixedVibration to, this time we're working with the alpha and scale properties.
  • The third parameter is the Spring of the FixedVibration, the higher the number the more 'springy' the movement. A value between 0 and 1 will work best.
  • Next up is the Ease of the vibration, the lower the value the quicker it will slide between the following two values. A value between 0 and 1 will work best.
  • The minimum value is up next, this will be the lowest the the vibration will hit.
  • Similarly, the maximum value will be the maximum value the vibration will hit.
  • Finally, the last parameter is relative, default this to false.

So how do all of these fit together? Let's look at how the scale property is affected by the FixedVibration. It's given Min and Max values of 0.05 and 0.8, the Spring value is pretty high at 0.9 and the Ease is pretty low at 0.05 making it scale erratically and fast.

Have a play around with these values to get a feel for how they influence the movement.

When we test our Flash file we should get something like this:


Step 11: Adding a VariableVibration

Very similar to the FixedVibration, the VariableVibration will adjust a property of an object with a value that fluctuates. The difference being that the VariableVibration isn't linear as the name suggests.

Amend your code as follows to place the clips to the center of the Stage, only this time we'll apply a VariableVibration to the x and y values to start seeing some movement!

1
2
	// each time we request a new shape from the pool

3
	// it will perform the following

4
	objectPool.onRequestObject = function(clip:MovieClip):void
5
	{
6
		clip.x = stage.stageWidth/2;
7
		clip.y = stage.stageHeight/2;
8
		
9
		// add a VariableVibration for the x/y movement of each circleShape

10
		var xVib:VariableVibration = new VariableVibration(clip, "x", 0.97, 0.03, 40);
11
		var yVib:VariableVibration = new VariableVibration(clip, "y", 0.97, 0.03, 40);
12
		xVib.start();
13
		yVib.start();
14
		
15
		// add a FixedVibration to the alpha and scale properties of each circleShape when requested

16
		var alphaVib:FixedVibration = new FixedVibration(clip, "alpha", 0.9, 0.05, 0.5, 1.5, false);
17
		var scaleVib:FixedVibration = new FixedVibration(clip, "scale", 0.9, 0.05, 0.05, 0.8, false);
18
		alphaVib.start();
19
		scaleVib.start();
20
		
21
		clipContainer.addChild(clip);
22
	}

Let's have a closer look at the VariableVibration objects we created. Each VariableVibration object we create takes only 5 parameters, respectively they are as follows:

  • The object to apply the VariableVibration to, in our case our circleShape called "clip".
  • The property to apply the VariableVibration to, this time we're working with the x and y properties.
  • The third parameter is the Spring of the vibration.
  • Next up is the Ease of the vibration.
  • The final parameter is the Range of values that is produced. The higher the number the more erratic the effect.

Our Flash file should look something like this when published:


Step 12: Adding Some Wow

It's starting to look good, but we can do much better! Remember that bitmapCanvas.startCapture() line I asked you to keep uncommented way back in Step 6? Go ahead and uncomment it then test your movie again.

This is more like it!


Step 13: A Quick Trick for Rotation

A very simple trick to add a spiraling movement is to add another vibration to the clip's rotation property like so:

1
2
	// each time we request a new shape from the pool

3
	// it will perform the following

4
	objectPool.onRequestObject = function(clip:MovieClip):void
5
	{
6
		clip.x = stage.stageWidth/2;
7
		clip.y = stage.stageHeight/2;
8
		
9
		// add a VariableVibration for the x/y movement of each circleShape

10
		var xVib:VariableVibration = new VariableVibration(clip, "x", 0.97, 0.03, 40);
11
		var yVib:VariableVibration = new VariableVibration(clip, "y", 0.97, 0.03, 40);
12
		xVib.start();
13
		yVib.start();
14
		
15
		// add a FixedVibration to the alpha and scale properties of each circleShape when requested

16
		var alphaVib:FixedVibration = new FixedVibration(clip, "alpha", 0.9, 0.05, 0.5, 1.5, false);
17
		var scaleVib:FixedVibration = new FixedVibration(clip, "scale", 0.9, 0.05, 0.05, 0.8, false);
18
		alphaVib.start();
19
		scaleVib.start();
20
		
21
		// add a FixedVibration to the rotation of the circleShape

22
		var rotationVib:FixedVibration = new FixedVibration(clip, "rotation", 0.9, 0.05, 0, 360, false);
23
		rotationVib.start();
24
		
25
		clipContainer.addChild(clip);
26
	}

Step 14: A Quick Trick for Rotation

Before testing this jump over to Flash and open the circleShape MovieClip in the Library.

As shown, drag the circle just off from center in any direction. The further you move it from center, the more spirals will appear in your design when you publish:

Offsetting the circleShape for spiralsOffsetting the circleShape for spiralsOffsetting the circleShape for spirals

Step 15: ExitShapes

One problem with our current animation is that once the clips move off the screen, they very rarely come back. We can solve this little problem by creating an ExitShapeTrigger.

An ExitShapeTrigger is an area considered safe for the clip. When the clip leaves this area we need to perform some kind of function, such as requesting a new clip from the ObjectPool.

We define an ExitShapeTrigger as below:

1
2
	// each time we request a new shape from the pool

3
	// it will perform the following

4
	objectPool.onRequestObject = function(clip:MovieClip):void
5
	{
6
		clip.x = stage.stageWidth/2;
7
		clip.y = stage.stageHeight/2;
8
		
9
		// add a VariableVibration for the x/y movement of each circleShape

10
		var xVib:VariableVibration = new VariableVibration(clip, "x", 0.97, 0.03, 40);
11
		var yVib:VariableVibration = new VariableVibration(clip, "y", 0.97, 0.03, 40);
12
		xVib.start();
13
		yVib.start();
14
		
15
		// add a FixedVibration to the alpha and scale properties of each circleShape when requested

16
		var alphaVib:FixedVibration = new FixedVibration(clip, "alpha", 0.9, 0.05, 0.5, 1.5, false);
17
		var scaleVib:FixedVibration = new FixedVibration(clip, "scale", 0.9, 0.05, 0.05, 0.8, false);
18
		alphaVib.start();
19
		scaleVib.start();
20
		
21
		// add a FixedVibration to the rotation of the circleShape

22
		var rotationVib:FixedVibration = new FixedVibration(clip, "rotation", 0.9, 0.05, 0, 360, false);
23
		rotationVib.start();
24
		
25
		// define an ExitShape

26
		var exit:ExitShapeTrigger = new ExitShapeTrigger(onExitShape, clip, exitShape, true);
27
		exit.start();
28
		
29
		clipContainer.addChild(clip);
30
	}
31
	
32
	// recycle objects

33
	private function onExitShape(clip:MovieClip):void
34
	{
35
		trace("circleShape left the screen!");
36
	}

This will create an ExitShapeTrigger with the following parameters:

  • The method to trigger when the event has occurred.
  • The MovieClip to test if it is out of the given area.
  • The MovieClip used to define the safe area, you might have already noticed we've already created this, called it exitShape and placed it on the Stage in Flash.
  • The last parameter is the Enter Once flag, just set this to true for now.

Step 16: ObjectPool Release

Following on from the ExitShape we introduced in the last step, we're going to simply edit the onExitShape method so that whenever a clip moves off the screen, we'll delete it and request a new one from the ObjectPool.

Until now we've been working with the request() and requestAll() methods of the ObjectPool, when we want to delete the old one we use the release(clip) method:

1
2
	// recycle objects

3
	private function onExitShape(clip:MovieClip):void
4
	{
5
		// remove from the ObjectPool and the clipContainer

6
		objectPool.release(clip);
7
		clipContainer.removeChild(clip);
8
		
9
		// get a new clip from the ObjectPool

10
		objectPool.request();
11
	}

Step 17: Adding a Blur

HYPE features the ability to very easily add filters such as blur and glow to objects. To add a touch more pizzazz to the design we're going to add a BlurFilter to the project using the FilterCanvasRhythm:

1
2
	// think of the BitmapCanvas as an empty space we will 'paint'

3
	// every frame with new image data

4
	bitmapCanvas = new BitmapCanvas(stage.stageWidth, stage.stageHeight);
5
	bitmapCanvas.startCapture(clipContainer, true);
6
	addChild(bitmapCanvas);
7
	
8
	// adding a blur

9
	var blur:FilterCanvasRhythm = new FilterCanvasRhythm([new BlurFilter(1.1, 1.1, 1)], bitmapCanvas);
10
	blur.start(TimeType.TIME, 100);
11
	
12
	// create a collection of objects and store them in an ObjectPool

13
	// for use in the animation

14
	objectPool = new ObjectPool(circleShape, 100);

The above code creates a FilterCanvasRhythm which takes a Filter as a parameter and applies it to the bitmapCanvas we declared earlier.

Test the project, it's really starting to come together now!


Step 18: Adding Some Diversity

We can easily add a little depth to the composition by adding more shapes in to the mix. Rather than having to create and manage several ObjectPools, we can add frames to the original circleShape we used and randomly select which frame to play.

Try this now, edit the circleShape object in the Flash Library. Create a new Keyframe, select a new color and draw a new shape. Go ahead and create a few Keyframes of your own, in the next step we'll look at implementing them with code. This is our new shape:

Offsetting the circleShape for spiralsOffsetting the circleShape for spiralsOffsetting the circleShape for spirals

..compared with our old shape:

Offsetting the circleShape for spiralsOffsetting the circleShape for spiralsOffsetting the circleShape for spirals

Step 19: Choosing Random Frames for circleShape

With our circleShape now sporting a few new Keyframes we can simply insert this single line of code to choose a frame to use each time we call objectPool.request():

1
2
	// each time we request a new shape from the pool

3
	// it will perform the following

4
	objectPool.onRequestObject = function(clip:MovieClip):void
5
	{
6
		clip.x = stage.stageWidth/2;
7
		clip.y = stage.stageHeight/2;
8
		
9
		// choose a random frame

10
		clip.gotoAndStop(Math.ceil(Math.random() * 3));
11
		
12
		// add a VariableVibration for the x/y movement of each circleShape

13
		var xVib:VariableVibration = new VariableVibration(clip, "x", 0.97, 0.03, 40);
14
		var yVib:VariableVibration = new VariableVibration(clip, "y", 0.97, 0.03, 40);
15
		xVib.start();
16
		yVib.start();
17
		
18
		// add a FixedVibration to the alpha and scale properties of each circleShape when requested

19
		var alphaVib:FixedVibration = new FixedVibration(clip, "alpha", 0.9, 0.05, 0.5, 1.5, false);
20
		var scaleVib:FixedVibration = new FixedVibration(clip, "scale", 0.9, 0.05, 0.05, 0.8, false);
21
		alphaVib.start();
22
		scaleVib.start();
23
		
24
		// add a FixedVibration to the rotation of the circleShape

25
		var rotationVib:FixedVibration = new FixedVibration(clip, "rotation", 0.9, 0.05, 0, 360, false);
26
		rotationVib.start();
27
		
28
		// define an ExitShape

29
		var exit:ExitShapeTrigger = new ExitShapeTrigger(onExitShape, clip, exitShape, true);
30
		exit.start();
31
		
32
		clipContainer.addChild(clip);
33
	}

As a quick note, the random frame code above will switch between frames 1, 2 and 3. You may need to adjust this to the amount of frames in your circleShape.


Step 20: Finish

It's time to test your movie and bask in the mixture of funky patterns and colors as the HYPE framework takes your code and mixes it into a piece of generative art.

Heres the final code for reference:

1
2
package  
3
{
4
	import hype.extended.behavior.FixedVibration;
5
6
	import flash.display.Sprite;
7
	import flash.display.MovieClip;
8
	import flash.filters.BlurFilter;
9
	import hype.extended.behavior.VariableVibration;
10
	import hype.extended.rhythm.FilterCanvasRhythm;
11
	import hype.extended.trigger.ExitShapeTrigger;
12
	import hype.framework.core.ObjectPool;
13
	import hype.framework.core.TimeType;
14
	import hype.framework.display.BitmapCanvas;
15
	
16
17
	/**

18
	 * A tutorial to introduce HYPE. A visual framework

19
	 * by Branden Hall and Joshua David for creating 

20
	 * generative / iterative design with code.

21
	 * 

22
	 * @author Anton Mills

23
	 * @version 06/02/2011

24
	 */
25
	public class Main extends MovieClip 
26
	{
27
		// private properties

28
		private var bitmapCanvas:BitmapCanvas;
29
		private var clipContainer:Sprite;
30
		private var objectPool:ObjectPool;
31
		
32
		
33
		
34
		/**

35
		 * constructor

36
		 */
37
		public function Main()
38
		{
39
			// inits the Hype framework

40
			initHype();
41
			
42
			// begin sequence

43
			objectPool.requestAll();
44
		}
45
		
46
		
47
		
48
		/**

49
		 * initiation of the Hype members we will be using and configuration prior

50
		 * to running the animation

51
		 */
52
		private function initHype():void
53
		{
54
			// the clipContainer is used as a parent for all of our sprites

55
			clipContainer = new Sprite();
56
			addChild(clipContainer);
57
			
58
			// think of the BitmapCanvas as an empty space we will 'paint'

59
			// every frame with new image data

60
			bitmapCanvas = new BitmapCanvas(stage.stageWidth, stage.stageHeight);
61
			bitmapCanvas.startCapture(clipContainer, true);
62
			addChild(bitmapCanvas);
63
			
64
			// adding a blur

65
			var blur:FilterCanvasRhythm = new FilterCanvasRhythm([new BlurFilter(1.1, 1.1, 1)], bitmapCanvas);
66
			blur.start(TimeType.TIME, 100);
67
68
			// create a collection of objects and store them in an ObjectPool

69
			// for use in the animation

70
			objectPool = new ObjectPool(circleShape, 100);
71
			
72
			// each time we request a new shape from the pool

73
			// it will perform the following

74
			objectPool.onRequestObject = function(clip:MovieClip):void
75
			{
76
				clip.x = stage.stageWidth/2;
77
				clip.y = stage.stageHeight/2;
78
			
79
				// choose a random frame

80
				clip.gotoAndStop(Math.ceil(Math.random() * 3));
81
				
82
				// add a VariableVibration for the x/y movement of each circleShape

83
				var xVib:VariableVibration = new VariableVibration(clip, "x", 0.97, 0.03, 40);
84
				var yVib:VariableVibration = new VariableVibration(clip, "y", 0.97, 0.03, 40);
85
				xVib.start();
86
				yVib.start();
87
				
88
				// add a FixedVibration to the alpha and scale properties of each circleShape when requested

89
				var alphaVib:FixedVibration = new FixedVibration(clip, "alpha", 0.9, 0.05, 0.5, 1.5, false);
90
				var scaleVib:FixedVibration = new FixedVibration(clip, "scale", 0.9, 0.05, 0.05, 0.8, false);
91
				alphaVib.start();
92
				scaleVib.start();
93
				
94
				// add a FixedVibration to the rotation of the circleShape

95
				var rotationVib:FixedVibration = new FixedVibration(clip, "rotation", 0.9, 0.05, 0, 360, false);
96
				rotationVib.start();
97
				
98
				// define an ExitShape

99
				var exit:ExitShapeTrigger = new ExitShapeTrigger(onExitShape, clip, exitShape, true);
100
				exit.start();
101
				
102
				clipContainer.addChild(clip);
103
			};
104
		}
105
		
106
		
107
		
108
		// recycle objects

109
		private function onExitShape(clip:MovieClip):void
110
		{
111
			objectPool.release(clip);
112
			clipContainer.removeChild(clip);
113
			
114
			objectPool.request();
115
		}
116
	}
117
}

Conclusion

This just about wraps up introducing the HYPE framework, thanks very much for your time. I hope you enjoyed it and remember we only scratched the surface of some of the fantastic effects that can be made with the framework. Please do check out the HYPE framework website at www.hypeframework.org for some fantastic examples of the framework and how others have taken it to the next level with Away3D/Papervision integration!


More HYPE Resources on Activetuts+

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.