<![CDATA[Tronicart]]>https://tronica.io/https://tronica.io/favicon.pngTronicarthttps://tronica.io/Ghost 5.75Sat, 20 Apr 2024 07:17:48 GMT60<![CDATA[Octies]]>https://tronica.io/octies/6583d28d51c1a55212f615dbThu, 21 Dec 2023 06:23:32 GMT

This is easily my most popular project to this day. One hundred unique code-generated octopuses published as an NFT collection.

The collection can be viewed on objkt.com.

How it was built

The technique in creating these is surprisingly simple, the main idea is to draw circles in a line while decreasing their size over time, rendering what basically looks like tentacles. The wavy aspect of each tentacle is easily achieved using a sine wave.

Octies

Once the basics are in place, it is fairly easy to tweak the generations to render cool looking octopuses. Some factors to consider are:

  • Amplitude of oscillations
  • Colour changes over the tentacle
  • Number of tentacles
  • Main direction of each tentacle

Examples

Here's a few of my favourite that came out of this project.

]]>
<![CDATA[FxHash: The sky march]]>https://tronica.io/fxhash-sky-march/6583d06351c1a55212f615b6Thu, 21 Dec 2023 05:49:50 GMT

This little sketch managed to sell out pretty much overnight on FxHash. It is a basic flow fields implementation that's been tuned to look a bit more spacey, definitely a crowd pleaser. Click on this link to try it out for yourself.

FxHash: The sky march
Sky march project on FxHash

Here are some of my favourite renders from it:

]]>
<![CDATA[Electronic museum]]>https://tronica.io/kinect-fields/6583c2f151c1a55212f6155fThu, 21 Dec 2023 05:22:03 GMT

A little project came together just in time to the 2023 Electronic Museum, an indie electronic music event in Hong Kong.

We built a fresh flow fields implementation which included emitter and attractor points, allowing us to direct lines to/from areas of the canvas. With a bit of work, we made it so those lines would be emitted from the mesh of a Kinect camera.

Here's an example for flow fields changing direction (before the Kinect integration) :

A summary video of the event was posted by the organisers here:

]]>
<![CDATA[Sherical coordinates]]>https://tronica.io/sherical-coordinates/6582a77d51c1a55212f614e3Wed, 13 Sep 2023 08:44:00 GMT

I'm playing around with the idea of creating a music visualiser in the shape of an Orb. I figured if I could generate a mesh of a sphere it would be a great start, and hopefully give me a reusable function which I could add to my library.

Let's build a function that'll generate Cartesian coordinates of a sphere. Our starting point for this is the Spherical Coordinates Wikipedia Article, on which I can find the following formula:

Sherical coordinates

Since we have 2 variables (theta & phi), this will translate in code to a double loop to iterate through all the angle combinations. Similar to polar coordinates but with an extra level to account for the 3rd dimension.

It looks something like this:

/**
 * Generates a set of spherical coordinates around a given center point.
 * 
 * @param {number} radius - The radius of the sphere.
 * @param {number} [resolution=100] - The number of points per full rotation. Higher values result in more detailed spheres.
 * @param {number} [x=0] - The x-coordinate of the sphere's center.
 * @param {number} [y=0] - The y-coordinate of the sphere's center.
 * @param {number} [z=0] - The z-coordinate of the sphere's center.
 * @returns {Array<Object>} data - A nested array of points on the sphere. Each point is an object with x, y, and z properties.
 */
function sphericalCoordinates(
  radius,
  resolution = 50,
  x = 0,
  y = 0,
  z = 0
) {
  const twoPi = Math.PI * 2;

  // The angular distance between each point
  const angleStep = twoPi / resolution;

  // Initialize the array that will store the generated points
  const data = [];

  for (let theta = 0; theta < twoPi; theta += angleStep) {
    const layer = [];

    for (let phi = 0; phi < twoPi; phi += angleStep) {
      // Convert spherical coordinates (radius, theta, phi) to Cartesian coordinates (x, y, z)
      layer.push({
        x: x + radius * Math.sin(theta) * Math.cos(phi),
        y: y + radius * Math.cos(theta),
        z: z + radius * Math.sin(theta) * Math.sin(phi)
      });
    }

    // Add this layer to the overall data array
    data.push(layer);
  }
  return data;
}

Once you have the points to draw, you can simply iterate over them and place them on the screen as such:

beginShape(POINTS);
for (const row of sphereData) {
  for (const item of row) {
    vertex(item.x, item.y, item.z)
  }
}
p5.endShape();

Here's an example of it in action:

]]>
<![CDATA[Isometric landscapes]]>https://tronica.io/isometric-landscapes/6583c13a51c1a55212f61541Mon, 10 Apr 2023 04:39:00 GMT

After creating a mini engine to make isometric art, I thought I'd put it to good use by generating some landscapes.

The main premise behind this piece is that every level of the landscape has a different colour, similar to real landscapes where higher levels would all be white for the snow, and lower levels blue for the water, etc...

Here are a few screen-grabs that came out of this generation:

Isometric landscapes
]]>
<![CDATA[TLDR: Isometric art]]>https://tronica.io/writing-a-simple-isometric-engine/6583900351c1a55212f6150aThu, 06 Apr 2023 01:09:00 GMT

Isometric perspective always a unique charm that I loved. In my toxic habit of rewriting everything from scratch, I began tinkering with the idea of building my own isometric engine, mostly for the sake of learning.

The basics

Isometry relies on a gentle illusion—taking a three-dimensional space and carefully placing it on a two-dimensional plane. The foundation of any isometric engine requires 2 main components:

  • a formula to translate 3D coordinates to 2D isometric ones
  • a method of drawing elements so they look right

I've summarised the approach in this easy to use flashcard:

The points to notice:

  • On the top left you will find a formula (the conversion of coordinates)
  • Drawing the cube has a few key points:
    • For a cube there should be 6 points evenly spaced on a circle
    • The angle between them would therefore be 2π / 6
    • Conversion of polar to cartesian coordinates is used to place the points
    • The side of the cube is equal to the tiling on the ground
]]>
<![CDATA[Wall art]]>https://tronica.io/wall-art/6583cbfe51c1a55212f6158cSat, 01 Apr 2023 05:26:00 GMT

This March 2023 saw the latest edition of the HK Walls event in Hong Kong 📍.
For the occasion I decided to try out coding some sketch that would look like it was spray painted on a wall.

I imagined a pretty simple idea of intersecting circles, that I would trace the outline of up until I ran into an intersection. At that point I would "hop" on the other circle and continue from there.

Here's an illustration of the main concept

Wall art
concept of intersecting circles for tracing shapes

With a bit of tweaking of the look and feel I managed to render these:

]]>
<![CDATA[Playing with gravity]]>https://tronica.io/playing-with-gravity/658257addcd3a6b09008b26dWed, 10 Aug 2022 02:56:00 GMT

After creating a basic simulation of orbital mechanics (as shown here), I ran a few renders with varying parameters, notably:

  • Colors
  • Number of celestial bodies
  • Weight of bodies
  • Gravity force
  • Gravity attraction between bodies

Here are a few renders that I thought were quite aesthetic:

Pencil on paper

Playing with gravity

An iris in the night sky

Playing with gravity

Water spiral

Playing with gravity
]]>
<![CDATA[Celestial bodies]]>https://tronica.io/celestial-bodies/658243983184cb63d36d46f6Wed, 13 Jul 2022 01:30:00 GMT

I wrote this sketch in 2022 from the comfort of my quarantine hotel.

It shows a common orbital mechanics implementation, simulating bodies in space orbiting around a single sun. I thought it would be cool to generate everything from scratch, including the background texture of the sky and the stars.

Creating the night sky texture

The background texture is based on a simple perlin noise map

Celestial bodies

Here's the pseudo-code to generate it:

for (let x = 0; x < width; ++x) {
  for (let y = 0; y < height; ++y) {
    const color = lerpColor(
      startColor,
      endColor,
      noise(x / 300, y / 300)
    );
    setPixel(col)
  }
}

Now swap the colours for something a bit more spacey I used:

startColor = color('#000000');
endColor = color('#030C34');
Celestial bodies
Perlin noise map with night sky colors

For the stars, the only trick was finding the right number of stars, one that would scale nicely regardless of the texture size, and the right colours. After doing some colour picking online I came up with the following values:

const pixelCount = width * height;
const starCount = random(pixelCount * 0.001, pixelCount * 0.003);

starColors = [
  color('#9bb0ff'),
  color('#aabfff'),
  color('#cad7ff'),
  color('#f8f7ff'),
  color('#fff4ea'),
  color('#ffd2a1'),
  color('#ffcc6f')
];

We were left with the following:

Celestial bodies

The gravity

Celestial bodies
]]>