buddhabrot-4d-viewer

A Buddhabrot 4D viewer for your browser

Usage no npm install needed!

<script type="module">
  import buddhabrot4dViewer from 'https://cdn.skypack.dev/buddhabrot-4d-viewer';
</script>

README

buddhabrot-4d-viewer-js

Explore the 4 dimensions of the Buddhabrot using this HTML5 + Javascript viewer.

Check out the live demo!

Features

  • Easy click & drag rotation.
  • RGB color channel selection.
  • Control the render using the 'Cancel', 'Repaint' and 'Reset' buttons.
  • Adjust the granularity of the scan ('Density' select).
  • Select the 4D rotation to render.
  • Histogram equalization for improved image quality.

Quick start

Options to add buddhabrot-4d-viewer-js to your project:

  • Install with npm: npm install buddhabrot-4d-viewer-js
  • Download the latest release
  • Clone the repo: git clone https://github.com/llop/buddhabrot-4d-viewer-js.git

Basic use

A working example can be found in index.html.

Include 'buddhabrot-4d-viewer.js' and 'buddhabrot-4d-viewer.css' in your HTML file.

<script src='buddhabrot-4d-viewer.js'></script>
<link rel='stylesheet' href='assets/buddhabrot-4d-viewer.css'>

Add the viewer's HTML elements. The following code is taken directly from the example:

<div class='buddhabrot'>
<canvas id='buddhabrot-canvas' class='buddhabrot-canvas' width='1000' height='600'>Your browser does not support canvas.</canvas>
<div class='buddhabrot-controls'>
  <div class='controls-table'>
  <div class='controls-table-row'>
    <div class='controls-table-cell'><div class='controls-label'>Colors</div></div>
    <div class='controls-table-cell'><div class='controls-label'>Volume</div></div>
  </div>
  <div class='controls-table-row'>
    <div class='controls-table-cell'>
      <div class='colors-box'>
        <input type='number' id='red-input' class='buddhabrot-input red-input' value='5000' min='1' max='5000'></input>
        <input type='range' id='red-slider' class='color-slider red-slider' value='5000' min='1' max='5000'></input>
      </div>
      <div class='colors-box'>
        <input type='number' id='green-input' class='buddhabrot-input green-input' value='500' min='1' max='5000'></input>
        <input type='range' id='green-slider' class='color-slider green-slider' value='500' min='1' max='5000'></input>
      </div>
      <div class='colors-box'>
        <input type='number' id='blue-input' class='buddhabrot-input blue-input' value='50' min='1' max='5000'></input>
        <input type='range' id='blue-slider' class='color-slider blue-slider' value='50' min='1' max='5000'></input>
      </div>
      <div class='controls-label image-label'>Image</div>
      <div class='buttons-box'>
        <button id='reset-btn' class='buddhabrot-button control-btn'>Reset</button>
        <div class='density-box'>
          <label for='density-select'>Density: </label>
          <select id='density-select' class='density-select buddhabrot-select'>
            <option value='low'>Low</option>
            <option value='standard' selected>Standard</option>
            <option value='hi'>High</option>
          </select>
        </div>
        <button id='repaint-btn' class='buddhabrot-button control-btn'>Repaint</button>
        <button id='cancel-btn' class='buddhabrot-button control-btn'>Cancel</button>
      </div>
    </div>
    <div class='controls-table-cell'>
      <div class='volume-controls'>
      <div class='volume-slider-box'>
        <input type='range' id='volume-slider' class='color-slider volume-slider' value='0' min='0' max='1000'></input>
      </div>
      <div class='volumes-box'>
        <img src='/img/fractals/buddhabrot-volumes.png' alt='Buddhabrot volumes' width='400' height='112'></img>
        <select id='volAX-select' class='volAX buddhabrot-select'>
          <option value='zr' selected>Zr</option>
          <option value='zi'>Zi</option>
          <option value='cr'>Cr</option>
          <option value='ci'>Ci</option>
        </select>
        <select id='volAY-select' class='volAY buddhabrot-select'>
          <option value='zr'>Zr</option>
          <option value='zi' selected>Zi</option>
          <option value='cr'>Cr</option>
          <option value='ci'>Ci</option>
        </select>
        <select id='volAZ-select' class='volAZ buddhabrot-select'>
          <option value='zr'>Zr</option>
          <option value='zi'>Zi</option>
          <option value='cr' selected>Cr</option>
          <option value='ci'>Ci</option>
        </select>
        <select id='volBX-select' class='volBX buddhabrot-select'>
          <option value='zr'>Zr</option>
          <option value='zi'>Zi</option>
          <option value='cr' selected>Cr</option>
          <option value='ci'>Ci</option>
        </select>
        <select id='volBY-select' class='volBY buddhabrot-select'>
          <option value='zr'>Zr</option>
          <option value='zi'>Zi</option>
          <option value='cr'>Cr</option>
          <option value='ci' selected>Ci</option>
        </select>
        <select id='volBZ-select' class='volBZ buddhabrot-select'>
          <option value='zr'>Zr</option>
          <option value='zi' selected>Zi</option>
          <option value='cr'>Cr</option>
          <option value='ci'>Ci</option>
        </select>
      </div>
      </div>
    </div>
  </div>
  </div>
</div>
</div>

All elements are, of course, customizable via CSS.

The initial image is the 'front' view of the (zr, zi, cr) object. Start the viewer with the following code:

<script>
window.addEventListener('load', event => {
  const canvas = document.getElementById('buddhabrot-canvas');
  const buddhabrot = new Buddhabrot(canvas);
  const buddhabrotControls = new BuddhabrotControls(buddhabrot, {
    redInput: document.getElementById('red-input'),
    greenInput: document.getElementById('green-input'),
    blueInput: document.getElementById('blue-input'),
    redSlider: document.getElementById('red-slider'),
    greenSlider: document.getElementById('green-slider'),
    blueSlider: document.getElementById('blue-slider'),
    volumeSlider: document.getElementById('volume-slider'),
    volAXSelect: document.getElementById('volAX-select'),
    volAYSelect: document.getElementById('volAY-select'),
    volAZSelect: document.getElementById('volAZ-select'),
    volBXSelect: document.getElementById('volBX-select'),
    volBYSelect: document.getElementById('volBY-select'),
    volBZSelect: document.getElementById('volBZ-select'),
    densitySelect: document.getElementById('density-select'),
    resetButton: document.getElementById('reset-btn'),
    repaintButton: document.getElementById('repaint-btn'),
    cancelButton: document.getElementById('cancel-btn')
  });
  buddhabrotControls.start();
});
</script>

Advanced use

It is recommended to learn about the Mandelbrot and the Buddhabrot rendering technique before changing the presets.

Buddhabrot class

The Buddhabrot class is in charge of scanning and rendering the fractal. Its constructor takes the HTML canvas (required).

You can also provide a custom options object to the Buddhabrot constructor in order to set the render parameters:

  • squareIters: How many points2 to sample within the area of a pixel.
  • maxNRed, maxNGreen, maxNBlue: For each color channel, set the maximum number of iterations for a point before it is considered to be in M.
  • histMaxN: To speed up scan times, a map is initially created to separate areas inside and outside of M. This value sets the maximum number of iterations for a point before it is considered to be in M in the aforementioned map. For consistency, it should be equal to max(maxNRed, maxNGreen, maxNBlue)
  • minN: Minimum number of iterations for a point before it is considered for plotting in the buddhabrot image.
  • brightness: Brightness multiplier.
  • colorCap: Maximum number of iterates per pixel per color channel to be considered for render.
  • waitMs: During a scan, this value sets how many ms before the next sleep cycle. This is necessary so as not to block the browser.
  • latitude, longitude: Determine the position of the observer.
  • volumeA, volumeB: The rendered 3D object can be volumeA, volumeB, or any rotation in between them. These values must be an array of the dimension's names.
  • angRot: Determines the rotation angles between volumeA's and volumeB's dimensions.

The defaults look like this:

{
  squareIters = undefined,
  maxNRed = 5000,
  maxNGreen = 500,
  maxNBlue = 50,
  histMaxN = 5000,
  minN = 1,
  brightness = 3.0,
  colorCap = 15000,
  waitMs = 100,
  latitude = 0.0,
  longitude = 0.0,
  angRot = [
      0.0,
      0.0,
      0.0
    ],
  volumeA = [ 
      'zr', 
      'zi', 
      'cr' 
    ],
  volumeB = [ 
      'cr', 
      'ci', 
      'zi' 
    ]
}

Render parameters can also be later changed using the following setters:

  • squareIters
  • setLatitudeLongitude(latitude, longitude)
  • setColorsMaxN(maxNRed, maxNGreen, maxNBlue)
  • setAngRot(angRotX, angRotY, angRotZ)
  • setVolumeA(x, y, z)
  • setVolumeB(x, y, z)

Once the a Buddhabrot instance is created, initialize() must be invoked. This function returns a Promise that will settle when everything is set up.

From then on, the Buddhabrot instance can be controlled via the following functions:

  • scan(callback): Sets off a scan for the currently specified parameters. callback must be a function, and it will be invoked when the scan finishes. callback will get one boolean argument success indicating if the scan completed or was canceled.

  • cancel(): Cancels the ongoing scan. It returns a Promise that will settle once the scan is cancelled. Does nothing if no scan is currently under way.

  • render(): Paints the available image data on the canvas.

Finally, there is a getter function painting to find out if a scan is in progress or not.

BuddhabrotControls class

The BuddhabrotControls class connects the UI to the Buddhabrot engine by using the aforementioned methods. Its constructor takes a Buddhabrot instance.

An options object can also be passed on to the constructor for customization. Besides the HTML elements, the following parameters are also available:

  • minNColor, maxNColor: Numeric bounds for the color channel inputs.
  • volumeSliderMax: Maximum value for the volumeA-volumeB slider input (minimum is 0).
  • progressWidth: Line width for the progress bar.
  • progressColor: Line color for the progress bar.
  • axesWidth: Line width for the axes.
  • axesColors: Array containing the line colors for the axes.

Default options are:

{
  minNColor = 1,
  maxNColor = 5000,
  volumeSliderMax = 1000,
  
  progressWidth = 4,
  progressColor = '#0f0',
  axesWidth = 3,
  axesColors = [ 
      '#9606b4', 
      '#ff8d12', 
      '#fff000' 
    ]
}

The start() function must be called to start up the viewer.

Events are dispatched whenever a scan starts or ends. It is best to add handlers before calling the start() function:

buddhabrotControls.on('scan-start', (event) => {
  console.log('scan-start', event);
});
buddhabrotControls.on('scan-end', (event) => {
  console.log('scan-end', event);
});
buddhabrotControls.start();

License

buddhabrot-4d-viewer-js is released under the MIT License. See LICENSE for details.