Async Generators

Generators can be async to leverage Web Workers for heavy computations.

Creating Async Generators

app.generatorRegistry.register('asyncPattern', {
  displayName: 'Async Pattern',
  description: 'Pattern using Web Workers',
  async: true,  // Mark as async

  params: {
    minDistance: { type: 'number', default: 30 },
    color: { type: 'color', default: '#8b5cf6' }
  },

  fn: async function(ctx) {
    const { params, async: asyncHelpers, patternGroup, view } = ctx;

    // Heavy computation in Worker
    const points = await asyncHelpers.poissonDiskSampling(
      view.size.width,
      view.size.height,
      params.minDistance
    );

    // Create visuals on main thread
    points.forEach(pt => {
      new paper.Path.Circle({
        center: [pt.x, pt.y],
        radius: 5,
        fillColor: params.color,
        parent: patternGroup
      });
    });
  }
});

Executing Async Generators

await app.generatorRegistry.executeAsync('asyncPattern', {
  minDistance: 40,
  color: '#ef4444'
});

Async Context Helpers

Available in ctx.async:

Method Description
poissonDiskSampling(w, h, dist) Even point distribution
goldenRatioDistribution(count, w, h) Spiral distribution
simplifyPath(points, tolerance) Reduce path points
calculateGeneratorPositions(config) Generic position calc

Example: Poisson Circles

app.generatorRegistry.register('poissonCircles', {
  displayName: 'Poisson Circles',
  async: true,

  params: {
    minDistance: { type: 'number', default: 50, min: 20, max: 100 },
    minRadius: { type: 'number', default: 10 },
    maxRadius: { type: 'number', default: 30 },
    colors: { type: 'array', default: ['#3b82f6', '#8b5cf6', '#ec4899'] }
  },

  fn: async function(ctx) {
    const { params, async: asyncHelpers, patternGroup, view } = ctx;

    // Worker computation
    const points = await asyncHelpers.poissonDiskSampling(
      view.size.width,
      view.size.height,
      params.minDistance
    );

    // Main thread rendering
    points.forEach((pt, i) => {
      const radius = params.minRadius +
        Math.random() * (params.maxRadius - params.minRadius);
      const color = params.colors[i % params.colors.length];

      new paper.Path.Circle({
        center: [pt.x, pt.y],
        radius: radius,
        fillColor: color,
        opacity: 0.7,
        parent: patternGroup
      });
    });
  }
});

Fallback Behavior

If Workers are unavailable, async helpers fall back to synchronous execution on the main thread.

// Check worker availability
if (ctx.async && app.workerPool.isAvailable()) {
  // Use async
  points = await ctx.async.poissonDiskSampling(w, h, dist);
} else {
  // Fallback to sync
  points = poissonDiskSampling(w, h, dist);
}

Performance Tips

  1. Use async generators for patterns with >100 elements
  2. Keep main-thread work (Paper.js) minimal
  3. Batch calculations in the worker when possible
  4. Consider progress indicators for long operations