art
generative
fxhash
Bezier 1

Bezier 1

written by reyrove

02 Oct 2024100 EDITIONS
1 TEZ

Setting Up the Basics

const ratio = 1 / 1;
const prefix = 'Bezier 1';

We start by defining a constant ratio, which is set to 1:1. This is used to maintain the aspect ratio of our canvas. We also define a prefix for our canvas, which will help in file naming for downloads.

const features = {};
let resizeTmr = null;
let thumbnailTaken = false;

Here, we declare an empty object features, which will later store visual attributes like colors and the number of curves. We also initialize variables like resizeTmr (used for debouncing resize events) and thumbnailTaken to track when the thumbnail is captured.

const urlSearchParams = new URLSearchParams(window.location.search);
const urlParams = Object.fromEntries(urlSearchParams.entries());
let forceDownloaded = false;

We fetch URL parameters using URLSearchParams and store them in urlParams. These can be used to pass certain configuration values through the URL. The forceDownloaded variable tracks whether the canvas has already been downloaded automatically.

const animated = false;
let nextFrame = null;

Here, animated is a flag indicating whether the canvas should be animated. For now, we set it to false. nextFrame will be used to store the next frame in case of animation.

Defining Parameters

definitions = [
  {
    id: "number_id1",
    name: "Number of Curves",
    type: "number",
    options: {
      min: 3,
      max: 50,
      step: 1,
    },
  },
  {
    id: "number_id2",
    name: "number Of Divisions",
    type: "number",
    options: {
      min: 4,
      max: 100,
      step: 1,
    },
  }
];
$fx.params(definitions);

We define two customizable parameters here: the number of curves and the number of divisions for the curves. These parameters can be adjusted between given minimum and maximum values and will later influence the way the curves are drawn.

Initializing the Canvas and Features

const setup = () => {
  const BackgroundColours = ['#040720', '#0C090A', '#34282C', '#151B54', '#033E3E', '#25383C', '#2C3539', '#004225', '#483C32', '#3B2F2F', '#43302E', '#551606', '#3F000F', '#2F0909', '#2B1B17', '#583759', '#36013F', '#2E1A47', '#342D7E'];
  const BackgroundrNames = ['Black Blue', 'Night', 'Charcoal', 'Night Blue', 'Deep Teal', 'DarkSlateGray', 'Gunmetal', 'Lotus Green', 'Taupe', 'Dark Coffee', 'Old Burgundy', 'Blood Night', 'Chocolate Brown', 'Dark Maroon', 'Midnight', 'Plum Purple', 'Deep Purple', 'Midnight Purple', 'Blue Whale'];
  const foregroundColours = ['#565051', '#C9C1C1', '#98AFC7', '#728FCE', '#0020C2', '#14A3C7', '#EBF4FA', '#57FEFF', '#77BFC7', '#01F9C6', '#5F9EA0', '#00A36C', '#808000', '#08A04B', '#E2F516', '#CCFB5D', '#FFFFCC', '#FBB117', '#FFA500', '#BCB88A', '#C9BE62', '#CD853F', '#966F33', '#665D1E', '#804A00', '#A0522D', '#C34A2C', '#B83C08', '#EB5406', '#C36241', '#FF5F1F', '#FA8072', '#FD1C03', '#C11B17', '#9F000F', '#CC7A8B', '#FFCBA4', '#F778A1', '#E75480', '#F6358A', '#FA2A55', '#F433FF', '#5E5A80', '#822EFF', '#C8C4DF', '#E9E4D4'];
  const foregroundNames = ['Vampire Gray', 'Steampunk', 'Blue Gray', 'Light Purple Blue', 'Cobalt Blue', 'Cyan Blue', 'Water', 'Blue Zircon', 'Blue Hosta', 'Bright Teal', 'CadetBlue', 'Jade', 'Olive', 'Irish Green', 'Yellow Green Grosbeak', 'Tea Green', 'Cream', 'Beer', 'Orange', 'Sage', 'Ginger Brown', 'Peru', 'Wood', 'Antique Bronze', 'Dark Bronze', 'Sienna', 'Chestnut Red', 'Ginger Red', 'Red Gold', 'Rust', 'Bright Orange', 'Salmon', 'Neon Red', 'Chilli Pepper', 'Cranberry', 'Dusky Pink', 'Deep Peach', 'Carnation Pink', 'Dark Pink', 'Violet Red', 'Red Pink', 'Bright Neon Pink', 'Grape', 'Blue Magenta', 'Viola', 'Ash White'];

  const BackgroundIndex = Math.floor($fx.rand() * BackgroundColours.length);
  const BackgroundColor = BackgroundColours[BackgroundIndex];

  features.backgroundColour = '#000000';
  features.Color1 = BackgroundColor;
  features.Color2 = foregroundColours;
  features.NumberofCurves = $fx.getParam('number_id1');

  const Color = [];
  const readableFeaturesObj = {};
  readableFeaturesObj['Background Color'] = BackgroundrNames[BackgroundIndex];

  for (let j = features.NumberofCurves; j > 0; j--) {
    const colorIndex = Math.floor($fx.rand() * features.Color2.length);
    Color[j] = features.Color2[colorIndex];
    const S = 'Foreground Color ' + j.toString();
    readableFeaturesObj[S] = foregroundNames[colorIndex];
  }

  features.Color3 = Color;

  readableFeaturesObj['Number of Curves'] = $fx.getParam('number_id1');
  readableFeaturesObj['number Of Divisions'] = $fx.getParam('number_id2');

  $fx.features(readableFeaturesObj);
  console.table(readableFeaturesObj);
};

This is the heart of the setup function where the colors and parameters are initialized:

Drawing the Bezier Curves

function drawSmoothClosedBezierCurve(ctx, points, LineWidth, Color) {
  if (points.length < 3) {
    console.error("At least three points are required to draw a closed curve.");
    return;
  }

  ctx.beginPath();
  ctx.moveTo((points[0][0] + points[points.length - 1][0]) / 2, (points[0][1] + points[points.length - 1][1]) / 2);

  for (let i = 0; i < points.length; i++) {
    let nextIndex = (i + 1) % points.length;
    let xc = (points[i][0] + points[nextIndex][0]) / 2;
    let yc = (points[i][1] + points[nextIndex][1]) / 2;
    ctx.quadraticCurveTo(points[i][0], points[i][1], xc, yc);
  }

  ctx.closePath();
  ctx.lineWidth = LineWidth;
  ctx.strokeStyle = Color;
  ctx.shadowBlur = LineWidth;
  ctx.shadowColor = Color;
  ctx.stroke();
}

This function draws smooth closed Bezier curves.

function anglesForEqualDivisions(ctx, numberOfDivisions) {
  const angle = (2 * Math.PI) / numberOfDivisions;
  const angles = [];
  for (let i = 0; i < numberOfDivisions; i++) {
    angles.push(i * angle);
  }
  return angles;
}

This helper function divides a circle into equal angles, ensuring that our curves are evenly spaced.

Finalizing the Drawing

const drawCanvas = async () => {
  window.cancelAnimationFrame(nextFrame);
  
  const canvas = document.getElementById('target');
  const ctx = canvas.getContext('2d');
  const w = ctx.canvas.width;
  const h = ctx.canvas.height;

  ctx.fillStyle = features.Color1;
  ctx.fillRect(0, 0, w, h);
  
  ctx.translate(w / 2, h / 2);
  const NumberofCurves = features.NumberofCurves;
  const numberOfDivisions = $fx.getParam('number_id2');
  
  for (let j = NumberofCurves; j > 0; j--) {
    const Color = features.Color3[j];
    const angle = anglesForEqualDivisions(ctx, numberOfDivisions);
    const points = [];
    for (let i = 0; i < angle.length; i++) {
      const Z = $fx.rand() * 3 / 4 + 1 / 4;
      points[i] = [Z * w / (2 * (j ** (1 / NumberofCurves))) * Math.cos(angle[i]), Z * h / (2 * (j ** (1 / NumberofCurves))) * Math.sin(angle[i])];
    }
    const LineWidth = w / (50 * (j ** (1 / NumberofCurves)) + numberOfDivisions);
    drawSmoothClosedBezierCurve(ctx, points, LineWidth, Color);
  }
  
  if (!thumbnailTaken) {
    $fx.preview();
    thumbnailTaken = true;
  }
  
  if ('forceDownload' in urlParams && !forceDownloaded) {
    forceDownloaded = true;
    await autoDownloadCanvas();
    window.parent.postMessage('forceDownloaded', '*');
  }

  if (animated) {
    nextFrame = window.requestAnimationFrame(drawCanvas);
  }
};

This is the drawCanvas function where the magic happens. We:

Putting it All Together

document.addEventListener('DOMContentLoaded', init);

Finally, we wait for the DOM content to load and trigger the init() function, which sets everything in motion.

With this article, I hope you've gained a deep understanding of how this Bezier curve generator works. Each part of the code works harmoniously to create smooth, random, and visually appealing curves, all while leveraging the power of JavaScript and HTML canvas.

Feel free to tweak the parameters, change the colors, or add new features to create your own version of this digital artwork. Whether it's adjusting the number of curves, playing with divisions, or experimenting with new color palettes, the possibilities are endless!

You can access the full code and explore this creative digital journey further. Click here to view and modify the complete code in our GitHub repository. Dive in, create your own versions, and don't forget to share your art with me on:

Your art will become part of our ever-growing digital tapestry, where creativity and code unite in harmony. Let’s inspire each other and show the world what love, creativity, and a bit of digital magic can do.

With endless love and creativity! 💙

_Frostbond Coders

stay ahead with our newsletter

receive news on exclusive drops, releases, product updates, and more

feedback