Liveweave: Generative AI Web Editor & HTML/CSS/JS Playground
Initializing
Liveweave
Web
expand_more
home
Home
data_object
CSS Explorer
arrow_outward
Palette
Color Explorer
arrow_outward
Polyline
Graphics Editor
arrow_outward
data_array
Python Editor
New
arrow_outward
build
Tools
expand_more
restart_alt
Load "Hello Weaver!"
post_add
Generate Lorem ipsum...
code
Format HTML
code_blocks
Format CSS
data_object
Format JavaScript
library_add
Library
expand_more
A
Algolia JS
Animate CSS
Apex Charts JS
B
Bulma CSS
Bootstrap
C
Chart JS
Chartist
Create JS
D
D3
Dojo
F
Foundation
Fullpage JS
G
Granim JS
Google Charts
H
Halfmoon
J
jQuery
M
Materialize
Moment JS
Masonry JS
Milligram CSS
P
Pure CSS
Primer CSS
Popper JS
Pattern CSS
Picnic CSS
R
React JS
Raphael JS
Raisin CSS
S
Semantic UI
Skeleton CSS
Spectre CSS
Tachyons CSS
T
Tailwind
Three JS
U
UI Kit
Vis JS
W
Water CSS
download
Download
expand_more
developer_mode
Download as HTML
folder_zip
Download as .ZIP
cloud_upload
Save
account_circle
Login
settings
Settings
expand_more
14
px
Live mode
Night mode
Line number
Mini map
Word wrap
sync_alt
Reset Settings
smart_display
Run
<!DOCTYPE html > <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>spread</title> <script type="text/javascript"> </script> </head> <body onload="setup()"> <p> Average ms per frame: <span id="frameratespan"></span> </p> <p> <input type="checkbox" id="cyclecheck" /> <label for="cyclecheck">Cycle colors</label> <input type="checkbox" id="fadecheck" /> <label for="fadecheck">Fade</label> <input type="checkbox" id="randomizecheck" /> <label for="randomizecheck">Randomize Position</label> <input type="checkbox" id="wrapcheck" /> <label for="wrapcheck">Wrap Edges</label> <br /> <label for="branchframestext">Spawn new every n frames:</label> <input type="text" id="branchframestext" size="3" /> </p> <canvas id="notepad"></canvas> <p> I love feedback. canvas@tomtheisen.com </p> </body> </html>
body { background-color: #111; font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; font-size: 12px; color: #808080;}
/* "Spread" tech demo of canvas by Tom Theisen * * This will animate a sequence of branch structures in a canvas element. * Each frame, a new direction is calculated, similar to the last frame, * but with a slight random perturbation. */ var start_width = 20; // starting width of each branch var frame_time = 30; // milliseconds per frame var straighten_factor = 0.95; // value from 0 to 1, factor applied to direction_offset every frame var curviness = 0.2; // amount of random direction change each frame var color_speed = 0.03; // speed at which colors change when cycling is enabled var branch_shrink = 0.95; // factor by which branches shrink every frame var min_width = 1; // minimum width for branch, after which they are discontinued var branch_opacity = 0.4; // opacity of lines drawn var width = 600; // width and height of canvas var height = 600; var branch_count = 3; // branch count per tree var branch_bud_size = 0.5; // ratio of original branch size at which branch will split var branch_bud_angle = 1; // angle offset for split branch; var paper; // reference to graphics context var branches = Object(); // linked list of active branches var color_styles = []; // pre-computed list of colors as styles. format: (r,g,b,a) var direction_offset = 0; // current direction offset in radians. this is applied to all branches. var frame = 0; // frame counter var timespent = 0; // total time spent so far, used to calculate average frame render duration var frameratespan; // html span element for updating performance number // preferences object, contains an attribute for each user setting var prefs = { wrap: true, // causes branches reaching edge of viewable area to appear on opposite side randomize: false, // randomize position of new branches fade: false, // fade existing graphics on each frame cycle: true, // gradually change colors each frame new_branch_frames: 20 // number of frames elapsed between each auto-generated tree }; // handle moust click. start new tree at mouse position function paper_click(event) { create_tree( branches, start_width, { x: event.pageX - paper.canvas.offsetLeft, y: event.pageY - paper.canvas.offsetTop }); } // create tree at the specified position with number of branches function create_tree(branches, start_width, position, branch_count) { var angle_offset = Math.PI * 2 / branch_count; for (var i = 0; i < branch_count; ++i) { branch_add(branches, new Branch(position, angle_offset * i, start_width)); } } // add branch to collection function branch_add(branches, branch) { branch.next = branches.next; branches.next = branch; } // get the coordinates for the position of a new tree // if not random, use the center of the canvas function get_new_tree_center(width, height, randomize) { return { x: (randomize ? Math.random() : 0.5) * width, y: (randomize ? Math.random() : 0.5) * height }; } // Branch constructor // position has x and y properties // direction is in radians function Branch(position, direction, width) { this.x = position.x; this.y = position.y; this.width = width; this.original_width = width; this.direction = direction; } // update position, direction and width of a particular branch function branch_update(branches, branch, paper) { paper.beginPath(); paper.lineWidth = branch.width; paper.moveTo(branch.x, branch.y); branch.width *= branch_shrink; branch.direction += direction_offset; branch.x += Math.cos(branch.direction) * branch.width; branch.y += Math.sin(branch.direction) * branch.width; paper.lineTo(branch.x, branch.y); paper.stroke(); if (prefs.wrap) wrap_branch(branch, width, height); if (branch.width < branch.original_width * branch_bud_size) { branch.original_width *= branch_bud_size; branch_add(branches, new Branch(branch, branch.direction + 1, branch.original_width)); } } function draw_frame() { var starttime = new Date(); if (prefs.fade) { paper.fillRect(0, 0, width, height); } if (prefs.cycle) { paper.strokeStyle = color_styles[frame % color_styles.length]; } if (++frame % prefs.new_branch_frames == 0) { create_tree(branches, start_width, get_new_tree_center(width, height, prefs.randomize), branch_count); } direction_offset += Math.random() * curviness - curviness / 2; direction_offset *= straighten_factor; var branch = branches; var prev_branch = branches; while (branch = branch.next) { branch_update(branches, branch, paper); if (branch.width < min_width) { // remove branch from list prev_branch.next = branch.next; } prev_branch = branch; } timespent += new Date() - starttime; frameratespan.textContent = (timespent / frame).toFixed(2); } // constrain branch position to visible area by "wrapping" from edge to edge function wrap_branch(branch, width, height) { branch.x = positive_mod(branch.x, width); branch.y = positive_mod(branch.y, height); } // for a < 0, b > 0, javascript returns a negative number for a % b // this is a variant of the % operator that adds b to the result in this case function positive_mod(a, b) { // ECMA 262 11.5.3: Applying the % Operator // remainder operator does not convert operands to integers, // although negative results are possible return ((a % b) + b) % b; } // pre-compute color styles that will be used for color cycling function populate_colors(color_speed, color_styles, branch_opacity) { // used in calculation of RGB values var two_thirds_pi = Math.PI * 2 / 3; var four_thirds_pi = Math.PI * 4 / 3; var two_pi = Math.PI * 2; // hue does represent hue, but not in the conventional HSL scheme for(var hue = 0; hue < two_pi; hue += color_speed) { var r = Math.floor(Math.sin(hue) * 128 + 128); var g = Math.floor(Math.sin(hue + two_thirds_pi) * 128 + 128); var b = Math.floor(Math.sin(hue + four_thirds_pi) * 128 + 128); color = "rgba(" + [r, g, b, branch_opacity].join() + ")"; color_styles.push(color); } } // wire up checkbox click event to prefs object attribute function wireup_checkbox(prefs, pref_name, id) { var checkbox = document.getElementById(id); checkbox.checked = prefs[pref_name]; checkbox.onclick = function() {prefs[pref_name] = this.checked;}; } // apply initial settings to canvas object function setup_canvas() { var canvas = document.getElementById("notepad"); canvas.width = width; canvas.height = height; paper = canvas.getContext("2d"); paper.fillStyle = "rgb(0, 0, 0)"; paper.fillRect(0, 0, width, height); paper.fillStyle = "rgba(0, 0, 0, 0.005)"; paper.strokeStyle = "rgba(128, 128, 64, " + String(branch_opacity) + ")"; document.getElementById("notepad").onclick = paper_click; } // called before any frames are drawn. initializes stuff. function setup() { populate_colors(color_speed, color_styles, branch_opacity); var checkboxes = ["fade", "cycle", "randomize", "wrap"]; for (var i = 0; i < checkboxes.length; ++i) { wireup_checkbox(prefs, checkboxes[i], checkboxes[i] + "check"); } setup_canvas(); frameratespan = document.getElementById("frameratespan"); var branchframestext = document.getElementById("branchframestext"); branchframestext.value = String(prefs.new_branch_frames); branchframestext.onblur = function() {prefs.new_branch_frames = parseInt(this.value);}; setInterval(draw_frame, frame_time); }
Check out the new AI-powered Python Playground
×