Get back to work gallery

VFX Trigger

3D, 2D and shader effects on one click or a key press

The code base https://github.com/LuisArmando-TestCoder/vfxtrigger

Published at https://vfxtrigger.netlify.app/



Shaders



2D Effects on key press

Press the keys list at the following file

3D Scene per page

More about all of this

Framework to set 2D effects on click

  • Under ./src/VFX/
  • Set the effect name. It could be something such as: nightSky/index.ts
  • Using Canvas Preset library for 2D (you can set up your own libraries; more about that down bellow) set your code like the following
export default presetObject => { return () => { } }
  • You can take advantage of the presetObject value like this
export default presetObject => { const getGlobalConfig = () => ({ sky: { color: '#fff', x: () => presetObject.c.width / 2, y: () => -presetObject.c.height / 4, maxRadius: 1, starsAmount: 750, rotor: { direction: -1, speedResistance: 42, }, }, }) const sky = createSky(getGlobalConfig, presetObject) return () => { presetObject.renderGroup("arc", sky.stars, sky.updateStar) } } function degreesToRadians(degrees) { return (degrees / 360) * (Math.PI * 2) } function createSky(getGlobalConfig, presetObject) { const globalConfig = getGlobalConfig.call(presetObject) const getRandomWidth = () => presetObject.random(presetObject.c.width) const getRandomHeight = () => presetObject.random(globalConfig.sky.y()) const getX = (step, randomWidth) => globalConfig.sky.x() + Math.sin(step) * randomWidth const getY = (step, randomWidth) => globalConfig.sky.y() + Math.cos(step) * randomWidth function getStar(index) { const { starsAmount } = this const step = degreesToRadians((360 / starsAmount) * index) const randomWidth = getRandomWidth() const randomHeight = getRandomHeight() const x = getX(step, randomWidth) const y = getY(step, randomWidth) const radius = presetObject.random(globalConfig.sky.maxRadius) + 1 return { x, y, radius, step, index, randomWidth, randomHeight, color: globalConfig.sky.color, } } function createStars( starsAmount ): { x: number y: number r: number step: number index: number randomWidth: number randomHeight: number color: string }[] { return [...new Array(starsAmount).keys()].map( getStar.bind({ starsAmount, }) ) } function updateStar(star) { star.step += degreesToRadians( ((star.radius * 2) / globalConfig.sky.rotor.speedResistance) * globalConfig.sky.rotor.direction ) star.x = getX(star.step, star.randomWidth) star.y = getY(star.step, star.randomWidth) } const stars = createStars(globalConfig.sky.starsAmount) return { stars, updateStar, } }
  • Under ./src/components/strings/Canvas/VFXDeclarations.ts you can now your effect like this:
import nightSky from "../../../VFX/nightSky" type TriggerMap = { [index: string]: ((data: { [index: string]: any }) => () => void)[] } export const functionToTriggers: TriggerMap = { t: [nightSky], } export const functionToNonAvailableTriggers: TriggerMap = { }

To add your own 2D library

  • Under ./src/components/strings/Canvas/index.tsx you can now replace the Canvas Preset setup
import React, { useEffect, useRef, useState } from "react" import preset from "canvas-preset" import declareVFXKeys from "./declareVFXKeys" import "./styles.scss" export default ({ className = "", id }: { className?: string; id: string }) => { const canvasRef = useRef(null) useEffect(() => { const presetObject = preset() presetObject.size() const triggerVFX = declareVFXKeys(presetObject) presetObject.draw(() => { triggerVFX() }) }, []) return <canvas ref={canvasRef} id={id} className={`canvas ${className}`} /> }

with your own

import React, { useEffect, useRef, useState } from "react" import Some2DLibraryConstructor from "some-2d-library" import declareVFXKeys from "./declareVFXKeys" import "./styles.scss" export default ({ className = "", id }: { className?: string; id: string }) => { const canvasRef = useRef(null) useEffect(() => { const some2DLibraryInstance = new Some2DLibraryConstructor() presetObject.size() const triggerVFX = declareVFXKeys(some2DLibraryInstance) ;(function yourProbableFunctionForAnimations() { triggerVFX() requestAnimationFrame(yourProbableFunctionForAnimations) })() }, []) return <canvas ref={canvasRef} id={id} className={`canvas ${className}`} /> }

Framework to add a shader

// https://www.shadertoy.com/view/Msl3WH export default ` // 'Warp Speed' by David Hoskins 2013. // I tried to find gaps and variation in the star cloud for a feeling of structure. // Inspired by Kali: https://www.shadertoy.com/view/ltl3WS void mainImage( out vec4 fragColor, in vec2 fragCoord ) { float time = (iTime+29.) * 60.0; float s = 0.0, v = 0.0; vec2 uv = (-iResolution.xy + 2.0 * fragCoord ) / iResolution.y; float t = time*0.005; uv.x += sin(t) * .3; float si = sin(t*1.5); // ...Squiffy rotation matrix! float co = cos(t); uv *= mat2(co, si, -si, co); vec3 col = vec3(0.0); vec3 init = vec3(0.25, 0.25 + sin(time * 0.001) * .1, time * 0.0008); for (int r = 0; r < 100; r++) { vec3 p = init + s * vec3(uv, 0.143); p.z = mod(p.z, 2.0); for (int i=0; i < 10; i++) p = abs(p * 2.04) / dot(p, p) - 0.75; v += length(p * p) * smoothstep(0.0, 0.5, 0.9 - s) * .002; // Get a purple and cyan effect by biasing the RGB in different ways... col += vec3(v * 0.8, 1.1 - s * 0.5, .7 + v * 0.5) * v * 0.013; s += .01; } fragColor = vec4(col, 1.0); } `

Framework to add a 3D scene per page

  • You need to create a .tsx page at ./src/pages/scenes/
  • Such as
// You are currently at ./src/pages/scenes/home.tsx import React from "react" import * as shaders from "../../shaders" import { GlobalWrapper, Canvas3D } from "../../components/strings" export default () => { return ( <GlobalWrapper title='Scenes | Home'> {/* Here you list all the scenes you want to overlap*/} <Canvas3D id="home" scenes={["home", "popinout"]}/> </GlobalWrapper> ) }
  • You can create a scene at ./src/scenes/ like this: /home/index.ts or just /home.ts
  • Now, using Scene Preset on top of THREE.js
import presetScene, { actions, types, consulters, events } from "scene-preset"; import * as THREE from "three"; // import rainbowMaterial from "../../materials/rainbow"; // import wavyMaterial from "../../materials/wavy"; // import liquidMetalMaterial from "../../materials/liquidMetal"; // import trippySpiralMetalMaterial from "../../materials/trippySpiral"; // import textureLogicMetalMaterial from "../../materials/textureLogic"; // import basicShaderToyMetalMaterial from "../../materials/basicShaderToy"; // import starfieldMaterial from "../../materials/starfield"; // import worleyNoiseWatersMaterial from "../../materials/worleyNoiseWaters"; actions.addSceneSetupIntrude( ({ presetConfiguration, camera }: { [index: string]: any }) => { presetConfiguration.ambient.color = 0x000000; presetConfiguration.camera.cameraVectorsState.top.acceleration.x *= 5; presetConfiguration.camera.cameraVectorsState.top.acceleration.z *= 5; presetConfiguration.camera.cameraVectorsState.friction.x *= 5; presetConfiguration.camera.cameraVectorsState.friction.z *= 5; camera?.setFocalLength(20); } ); export default (id: string) => presetScene( { async setup(canvasState: { [index: string]: any }) { [ // rainbowMaterial, // wavyMaterial, // liquidMetalMaterial, // trippySpiralMetalMaterial, // textureLogicMetalMaterial, // basicShaderToyMetalMaterial, // starfieldMaterial, // worleyNoiseWatersMaterial, ].forEach((material) => { actions.setUniforms(material); }); let wasRecording = false; let recorder = consulters.getCanvasRecorder( canvasState.canvas as HTMLCanvasElement ); actions.downloadCanvasRecordingOnStop(recorder); events.onKey("g").end(() => { console.log("hey") recorder[wasRecording ? "stop" : "start"](); wasRecording = !wasRecording; if (!wasRecording) { recorder = consulters.getCanvasRecorder( canvasState.canvas as HTMLCanvasElement ); actions.downloadCanvasRecordingOnStop(recorder); } }); }, animate(canvasState: { [index: string]: any }) { // actions.blacklistObjects({ // scene: canvasState.scene as THREE.Scene, // blacklist: [ // "SimpleFloor", // "SimpleCube", // "SimpleSphere", // "SimpleLightSet", // ], // }); }, }, `#${id}` );
  • Also, reference this file at ./src/scenes/index.ts