demolished-rail

This repository contains a TypeScript animation framework called demolished-rail, designed for creating complex and dynamic animations with ease. The framework provides a structured approach to building animations using scenes, entities, and a sequence controller. It also includes features like BPM synchronization, beat and tick events, and audio analysis (FFT) to create interactive and audio-reactive visualizations.

Features

Installation

 npm  install demolished-rail

Usage

Create a Sequence with scene and entities

1. Create a Sequence:

Create an instance of the Sequence class, providing the target canvas, BPM, time signature, audio loader, and an optional array of scenes. Initialize the sequence using the initialize() method. This will load the audio and prepare the sequence for playback.

2. Create Scene objects:

Create instances of the Scene class, defining the name, start time, and duration of each scene. Add entities to each scene using the addEntity() or addEntities() methods.

3. Add scenes to the Sequence:

Use the addScene() or addScenes() methods of the Sequence to add your scenes to the animation sequence.

4. Start the animation:

Call the play() method on the Sequence instance to start the animation.

Example code

     import { Scene, Sequence, DefaultAudioLoader, Entity } from 'demolished-rail';
      // ... import other classes and effects ...
      
    const canvas = document.getElementById('myCanvas') as HTMLCanvasElement;
    const audioLoader = new DefaultAudioLoader('/path/to/your/audio.mp3'); // Use DefaultAudioLoader
    const sequence = new Sequence(canvas, 120, 4, 4, audioLoader);
    
    await sequence.initialize();
    
    const scene1 = new Scene("Scene 1", 0, 10000);
    
    // Example entity (replace with your actual entities)
    const myEntity = new Entity(
      "MyEntity",
      { 
        // ... entity properties ...
      },
      (ts, ctx, props) => {
        // ... entity action ...
      }
    );
    scene1.addEntity(myEntity);
    
    // ... create and add other scenes and entities ...
    
    sequence.addScenes(scene1 /*, ... other scenes */);
    
    sequence.play();

Creating a Canvas2D Entity in Demolished-rail

Demolished-rail provides a flexible way to create and animate entities using the HTML5 Canvas API. Here’s how to set up a Canvas2D entity in your production:

1. Define Entity Properties

2. Create the Entity

Example Code

	import { Entity } from 'demolished-rail';
    
    // Define entity properties
    interface ICircleProps {
      x: number;
      y: number;
      radius: number;
      color: string;
    }
    
    // Create the entity
    const circleEntity = new Entity<ICircleProps>(
      "MyCircle",
      {
        x: 100,
        y: 100,
        radius: 50,
        color: 'red'
      },
      (ts, ctx, props) => {
        // Draw a circle on the canvas
        ctx.beginPath();
        ctx.arc(props.x, props.y, props.radius, 0, 2 * Math.PI);
        ctx.fillStyle = props.color;
        ctx.fill();
      }
    );
    
    // Add the entity to a scene
    someScene.addEntity(circleEntity);

Creating a WebGL Shader Entity in Demolished-Rail

Demolished-rail also supports WebGL, allowing you to create shader entities using GLSL (OpenGL Shading Language). Here’s how to set up a WebGL shader entity in your production: 1. Shader Properties

2. Create the Shader Entity

Example Code

    import { 
      DefaultAudioLoader,
      Entity,
      GLSLShaderEntity,
      IEntity,
      Scene,
      Sequence,
      SequenceHelper,
    } from 'demolished-rail';
    import { earthShader } from '../assets/shaders/earthShader';
    import { mainFragment } from '../assets/shaders/mainFragment';
    import { mainVertex } from '../assets/shaders/mainVertex';
    
    // ... (inside your demo setup) ...
    
    const cameraPositions = [
      [0.0, 1.2, 0.7],
      [0.5, 1.0, 0.9],
      // ... more camera positions
    ];
    let cameraPos = cameraPositions[0];
    let amountOfLightning = 1500.0;
    
    const shader = new GLSLShaderEntity<IEarthShader>(
      "earthShader",
      {
        cameraPos: cameraPositions[0],
        amountOfLightning: 1500,
        mainFragmentShader: mainFragment,
        mainVertexShader: mainVertex,
        renderBuffers: [
          {
            name: "a_buffer",
            fragment: earthShader,
            vertex: mainVertex,
            textures: [],
            customUniforms: {
              "amountOfLightning": (uniformLocation, gl, program, time, entity) => {
                gl.uniform1f(uniformLocation, entity.props!.amountOfLightning);
              },
              "cameraPos": (uniformLocation, gl, program, time, entity) => {
                gl.uniform3fv(uniformLocation, entity.props!.cameraPos);
              }
            }
          }
        ]
      },
      (ts, render, propertybag) => {
        // ... your additional logic for the shader entity ...
      },
      800, // Canvas width
      450  // Canvas height
    );
    
    shader.onBar<IEarthShader>((ts, count, propertyBag) => {
      const positionIndex = count % cameraPositions.length;
      propertyBag!.cameraPos = cameraPositions[positionIndex];
    });
    
    shader.onTick<IEarthShader>((ts, count, propertyBag) => {
      // ... update amountOfLightning based on audio ...
    });
    
    // Add the entity to a scene
    someScene.addEntity(shader);

Building a WebGPU Shader Entity in Demolished-Rail

Demolished-rail now includes support for WebGPU, allowing you to create high-performance shader entities using the WGSL (WebGPU Shading Language). Here’s a breakdown of how to set up a WebGPU shader entity in your production:

1. Initialization

2. Shader Properties

3. Create the Shader Entity

Example Code

    import {
      defaultMainShader,
      Geometry,
      IWGSLShaderProperties,
      Material,
      WGSLShaderEntity,
      WGSLTextureLoader,
      WGSLTextureType,
    } from 'demolished-rail';
    import { rectGeometry } from 'demolished-rail/Engine/ShaderRenderers/WebGPU/Geometry';
    import { initWebGPU } from 'demolished-rail/Engine/ShaderRenderers/WebGPU/WGSLShaderRenderer';
    import { wgslFlamesShader } from '../assets/shaders/wglsl/wgslFlamesShader';
    
    // ... (inside your demo setup) ...
    
    const wgslCanvas = document.createElement("canvas");
    wgslCanvas.width = 800; // Set your desired width
    wgslCanvas.height = 450; // Set your desired height
    
    const webgpu = await initWebGPU(wgslCanvas);
    
    const wsglTextures = await WGSLTextureLoader.loadAll(webgpu.device, {
      key: "NOISE-TEXTURE",
      source: "assets/images/noise.png",
      type: WGSLTextureType.IMAGE,
    });
    
    const wgslShaderProps: IWGSLShaderProperties = {
      canvas: wgslCanvas,
      device: webgpu.device,
      context: webgpu.context!,
      shader: defaultMainShader,
      renderBuffers: [
        {
          name: "buffer-01",
          shader: new Material(webgpu.device, wgslFlamesShader),
          geometry: new Geometry(webgpu.device, rectGeometry),
          textures: wsglTextures
        }
      ]
    };
    
    const wgslShaderEntity = new WGSLShaderEntity(
      "wgsl-shader",
      wgslShaderProps,
      (ts: number, wgslRenderer: WGSLShaderRenderer, props: IWGSLShaderProperties) => {
        // Perform operations, like modifying uniforms, per frame
      }
    );
    
    // Add the entity to a scene
    someScene.addEntity(wgslShaderEntity)

Contributing

Contributions are welcome! Feel free to open issues or submit pull requests.