Pen Settings



CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ add another resource


Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource


Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.


Auto Save

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.


                <!doctype html>
		<meta charset="utf-8" />
		<title>ZIM Line</title>

		<!-- Welcome to ZIM at - Code Interactive Media Pizzazz!             -->
		<!-- ZIM runs on the HTML Canvas powered by JavaScript and CreateJS -->
		<!-- Founded by Inventor Dan Zen - - Canadian New Media Award Winner     -->
		<!-- ZIM is free to use. You can donate to help improve ZIM at     -->

		<script src=""></script>
		<script src=""></script>
		<!-- use for minified individual functions! -->

		<script src=""></script>


			// scaling can have values as follows with full being the default
			// "fit"    sets canvas and stage to dimensions and scales to fit inside window size
			// "outside"    sets canvas and stage to dimensions and scales to fit outside window size
			// "full"    sets stage to window size with no scaling
			// "tagID"    add canvas to HTML tag of ID - set to dimensions if provided - no scaling

			const scaling = "banner"; // target the banner tag
			const width = 1000;
			const height = 150;
			const color = dark;
			const outerColor = dark;
			// as of ZIM 5.5.0 you do not need to put zim before ZIM functions and classes
			var frame = new Frame(scaling, width, height, color, outerColor);
			frame.on("ready", ()=>{
				zog("ready from ZIM Frame"); // logs in console (F12 - choose console)

				const stage = frame.stage;
				const stageW = frame.width;
				const stageH = frame.height;
				// the general idea is we have two ZIM Frame objects each with their own canvas and stage 
				// the first we see to start and it is scaled to fit in the div with id=banner 
				// this frame is interactive 
				// the second is a full window frame and it is not interactive
				// this allows us to interact with the HTML 
				// we made it not interactive by turning off the canvas pointerEvents 
				// we hide and show the content when we press anywhere on the banner 
				// we could have made a button, etc.
				// BTW -	we can certainly make objects travel along paths see
				// here we wanted to give the illusion of a continuous path so used ZIM Noise.

				// make the animated backing lines with ZIM Pizzazz - see imported js file for details
					colors:series([pink, purple]),
					.sca(-1,1) // goes in other direction normally
					.cur() // pass in any css cursor - default "pointer"

				const icon = frame.makeIcon(null, "#222").pos(20,0,LEFT,CENTER);
				// used the icon size and position but want the backing behind the icon so ord(-1)
				// will animate this in later
				const backing = new Rectangle(icon.width, icon.height, pink).loc(icon).mov(6,6).ord(-1).alp(0);
				new Label("FOLLOW", 100, "impact", white).alp(.5).noMouse().pos(icon.width+45,16,LEFT,CENTER);

				// constants for animated line				
				const bumps = 7; // number of bumps
				const curve = 60; // "radius" of Bezier handle
				const size = 75; // height from center - so max is roughly twice this
				const step = .01; // bigger for faster speed

				// we make lots of times eventually so make a class
				// in the first case it gets a grey fill in the other cases it does not 
				// segments is what line segment to put the triangle arrow at 
				// and we are creating this from two different frames with different stages 
				// if we want the Ticker inside to update the second stage we need the stage
				class Line extends Container {
					constructor(w, h, fill=true, segment=4, stage) {
						super(w, h); // make the ZIM Container

						// the shape object
						// this.shape if we want to reference it outside (we do not need to in this case)
						// this is a reference to the container we have extended 
						// which will also hold all the stuff we add here and be the object made from this class
						const shape = this.shape = new Shape(w, h).addTo(this); 

						// the noise object
						const noise = this.noise = new Noise(); 

						let j = 0; // second parameter for simplex2D
						let count = 0; // ticker count

						const arrow = this.arrow = new Triangle(40,40,40,white,pink,3).rot(-90).addTo(this);

						// we will wiggle the arrow between two points
						// this dist will give us a ratio moving at least .2 and no more than .5 from the .5 start
						// in a time of at least 1000 and no more than 5000 ms
						// store a custom property on the arrow to wiggle
						arrow.dist = .5;
						arrow.wiggle("dist", .5, .2, .5, 1000, 5000);

						// Ticker runs fast
						Ticker.add(function() {
							// see for a bunch of noise examples
							j += step; // increase second parameter by just a little to animate the line
							var lastX = -5; // just off stage so can't see pink border
							var lastY = h/2;

							// zim provides tiny graphics API on the Shape
							// To use full names like clear(), beginStroke(), setStrokeStyle(), beginFill(),
							// moveTo(), lineTo(), bezierCurveTo(), etc. 
							// put these on the graphics property of the shape
							// for instance, etc. blah
							// The fill still has the pink stroke which we do not want to see 
							// so make this go off stage to the left, right and bottom
							if (fill) shape.c().s(pink).ss(3).f(dark).mt(-5,w+5).lt(lastX, lastY);
							else shape.c().s(pink).ss(3).lt(lastX, lastY); // no fill

							loop(bumps, function(i) {
								var x = (i+1)*w/bumps+5; // +5 just makes sure the last one is off the stage
								var y = h/2 + noise.simplex2D(i,j)*size;, lastY, x-curve, y, x, y);
								if (i==segment) {
									var points = [{x:lastX, y:lastY}, {x:lastX+curve,y:lastY}, {x:x-curve,y:y}, {x:x,y:y}];
									var data = pointAlongCurve(points, arrow.dist, true);
								lastX = x;
								lastY = y;
							if (fill),h+5); // finish the fill versions
						}, stage);

				// the shape for the line with fill - we see this to start
				new Line(stageW, stageH, true, 4, stage).addTo().bot().ord(1);

				// toggle the other lines on the second stage - see below
				stage.on("stagemousedown", ()=>{
					if (lines.getChildAt(0).alpha == 0) { // we can't see the lines so show them
							props:{alpha:{min:.2, max:1}},
						backing.animate({alpha:1}, 1000);
					} else { // we can see the lines so hide them
							call:() => {
								lines.loop(line=>{ // randomize the line angles for next time
						backing.animate({props:{alpha:0}, time:700, wait:1200});

				// the second frame which is full window (Frame defaults to full)
				const frame2 = new Frame();
				let lines = null; // declare outside as we use this above
				frame2.on("ready", function () {
					// make the canvas not interactive
					// otherwise would not be able to select html text or click links, etc. = "none"; 

					const stage = frame2.stage;
					lines = new Container(frame2.width, frame2.height).addTo(stage);

					loop(10, ()=>{
						// false is no fill and 2-5 is which segment to put the arrow
						new Line(stageW, stageH, false, rand(2,5), stage) 
							.reg(0) // move the regX back to 0
							.alp(0) // we will animate them in
							.loc(0,0) // move lines to corner
							.rot(rand(10,70)); // rotate randomly
					frame2.on("resize", () => {
						// the full mode does not scale 
						// so we need to position manually to match the scaling of the banner 
						// note, because ZIM Retina, there is actually a scale to the second frame

			}); // end of ready

			// docs for items used:

			body {margin:0px;}			
			#bannerCanvas {
				width:100% !important; /* not sure what is going on with codepen! */
				height:100% !important; /* should only need width:100% */
			main {
				line-height: 25px;
			main a {
				padding:1px 4px;
				text-decoration: none;
			main a:hover {
				background-color: yellow;
			main img {height:120px;}

		<meta name="viewport" content="width=device-width, user-scalable=no" />


		<div id="banner"></div>
			<p>This is normal HTML content.  Click the banner to toggle a surprise!</p>
				ZIM is having a contest with $2020 prizes - for real.
				Come on over to <a href="" target="_blank"></a> and check it out!
				There are ten $202 prizes for the best Zapp! each month.
				A Zapp! is a quick game, puzzle, art, app, etc. made with ZIM.
				You can make interactive works so easily!
				Please visit the CodePen Topics page at <a href="" target="_blank"></a>

			<img src="" alt="Dr Abstract from ZIM">