Play around with the Chaos game!

Customize the "next point" code
Write your own code to calculate where the next point should be, by setting the nextPoint variable. nextPoint must be a vector, specifically a MathJS Matrix object like: [100, 200], or a complex number like complex(100, 200). The default code simply picks one of the targets at random and moves the current point halfway there, but you can change that using functions, operators, and whatever else you can find in MathJS. You can use the coordinates of the current point, currentPoint (a vector), and the coordinates of each target, targetPoints, (a matrix).
Show examples

Examples:

Add 1 to both x and y:
nextPoint = currentPoint + 1
With rotation:
nextTargetIndex = math.randomInt(1, targetPointsLength+1)
nextTarget = targetPoints[nextTargetIndex, :]
midpoint = (currentPoint + nextTarget) / 2
rotation = [[math.cos(math.pi/4), -math.sin(math.pi/4)], [math.sin(math.pi/4), math.cos(math.pi/4)]]
nextPoint = math.multiply(midpoint, rotation)
Show advanced
  • write() function Write text to the page. Example: write("currentPoint:", currentPoint)
  • nextPointColor If you set the variable nextPointColor to "red" or "rgb(255, 0, 0)" or [255, 0, 0] the next point will be plotted in red instead of black. Example: nextPointColor = [111, 200, 15].
  • Technique: saving data for the next iteration Variables set in one iteration are available in subsequent iterations. See "Avoid previous target" in the advanced examples, below.
The following functions are usually used in the optional initialization code:
  • createSlider() function Create a slider that controls a number. Example: t = createSlider("some number", -1, 4, 2) will create a slider ranging over -1 to 4 and starting at 2. Then ex. nextPoint = currentPoint + t in your code gives your end user 'live' control over the scalar value, t. Optionally you can specifiy a 5th parameter which, when false, prevents the existing points from being cleared when the user slides the slider. Example: slider = createSlider("rotation", 0, 6.28, 0, false). You can also pass arguments to noUiSlider.create() by using a Javascript object as the 5th parameter, like {step: 0.01} to set a custom step increment.
  • setTargetsCount(), setPointsCount(), and setOpacity() functions Set the fields seen above, programmatically. Example: setTargetsCount(5) will set the Targets field to 5.
  • getTargetsCount(), getPointsCount(), and getOpacity() functions Get the current values of the fields seen above. Example: n = getTargetsCount().
  • zoom() and pan() functions Set the zoom level or center of view fields (see below) programmatically.
Advanced example - The Levy C curve fractal:
zoom(325)
A = matrix([[.5,  .5], [-.5, .5]])
B = matrix([[.5, -.5], [ .5, .5]])
point1 = multiply(A, currentPoint)
point2 = multiply(B, currentPoint) - matrix([.5, .5])
nextPoint = randomInt() ? point1 : point2
Advanced example - Avoid previous target:
# Put this chunk in the initialization code pane
previous_target = -1  # start with a non-existing target
slider = createSlider("how far to go toward the target", -2, 1.4, 0.5)

# Put the rest of the code in the main pane
# Get all possible indices except the target we used in the last iteration
possibleTargetIndices = math.range(1, targetPointsLength+1)
possibleTargetIndices = possibleTargetIndices[possibleTargetIndices != previous_target]
nextTargetIndex = math.pickRandom(possibleTargetIndices)
nextTarget = targetPoints[nextTargetIndex, :]
nextPoint = currentPoint + (nextTarget - currentPoint) * slider
# Store current target for next iteration
previous_target = nextTargetIndex
Advanced example - The Corcoran Attractor:
# Put this chunk in the initialization code pane
setTargetsCount(12)
setPointsCount(3000000)
setOpacity(0.2)
phase_offset = createSlider("Phase offset", 0, 2*math.pi, math.pi*29/25)
amplitude = createSlider("Amplitude", -3/2, 4, 1/3)
bias = createSlider("Bias", -1, 7/4, 1/2)

# Put the rest of the code in the main pane
nextTargetIndex = math.randomInt(1, targetPointsLength+1)
nextTarget = targetPoints[nextTargetIndex, :]
displacement_factors = math.matrix([math.sin(nextTarget[2] + phase_offset) * amplitude + bias, math.sin(nextTarget[1] + phase_offset) * amplitude + bias])
# Apply the factors to the displacement
displacement_to_target = nextTarget - currentPoint
nextPoint = currentPoint + math.dotMultiply(displacement_to_target, displacement_factors)

source   3D version