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 an array of two numbers [100, 200], a matrix [[100, 200]], or a complex number 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, and the coordinates of each target, targetPoints. Both are matrices.
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 = transpose(multiply(A, transpose(currentPoint)))
point2 = transpose(multiply(B, transpose(currentPoint)) - transpose(matrix([[.5, .5]])))
nextPoint = randomInt() ? point1 : point2
Advanced example - Avoid previous target:
# IMPORTANT! Put these two lines in the "Optional 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)

# The rest of this code goes 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 these in the initialization code pane
setTargetsCount(12)
setPointsCount(3000000)
setOpacity(0.5)


# Create user controls for the parameters
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)

nextTargetIndex = math.randomInt(1, targetPointsLength+1)
nextTarget = targetPoints[nextTargetIndex, :]

displacement_factors = math.matrix([
  [math.sin(nextTarget[1,2] + phase_offset) * amplitude + bias,
   math.sin(nextTarget[1,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