diff --git a/js/Matrix.js b/js/Matrix.js
index 11979c9..653ac90 100644
--- a/js/Matrix.js
+++ b/js/Matrix.js
@@ -107,34 +107,32 @@ import makeConfig from "./utils/config";
/** @param {MatrixProps} props */
export const Matrix = memo((props) => {
- const { style, className, ...rest } = props;
- const elProps = { style, className };
- const matrix = useRef(null);
- const rainRef = useRef(null);
- const canvasRef = useRef(null);
+ const { style, className, ...rest } = props;
+ const elProps = { style, className };
+ const matrix = useRef(null);
+ const rainRef = useRef(null);
+ const canvasRef = useRef(null);
- useEffect(() => {
- const canvas = document.createElement("canvas");
- canvas.style.width = "100%";
- canvas.style.height = "100%";
- canvasRef.current = canvas;
- }, []);
+ useEffect(() => {
+ const canvas = document.createElement("canvas");
+ canvas.style.width = "100%";
+ canvas.style.height = "100%";
+ canvasRef.current = canvas;
+ }, []);
- useEffect(() => {
- matrix.current.appendChild(canvasRef.current);
- const gl = canvasRef.current.getContext("webgl");
- createRain(canvasRef.current, makeConfig({ ...rest }), gl).then(
- (handles) => {
- rainRef.current = handles;
- }
- );
+ useEffect(() => {
+ matrix.current.appendChild(canvasRef.current);
+ const gl = canvasRef.current.getContext("webgl");
+ createRain(canvasRef.current, makeConfig({ ...rest }), gl).then((handles) => {
+ rainRef.current = handles;
+ });
- return () => {
- if (rainRef.current) {
- destroyRain(rainRef.current);
- }
- };
- }, [props]);
+ return () => {
+ if (rainRef.current) {
+ destroyRain(rainRef.current);
+ }
+ };
+ }, [props]);
- return
;
+ return ;
});
diff --git a/js/index.js b/js/index.js
index 2a5be3b..67e6a5a 100644
--- a/js/index.js
+++ b/js/index.js
@@ -6,43 +6,43 @@ import { Matrix } from "./Matrix";
const root = createRoot(document.getElementById("root"));
let idx = 1;
const versions = [
- "3d",
- "trinity",
- "bugs",
- "megacity",
- "nightmare",
- "paradise",
- "resurrections",
- "operator",
- "holoplay",
- "throwback",
- "updated",
- "1999",
- "2003",
- "2021",
+ "3d",
+ "trinity",
+ "bugs",
+ "megacity",
+ "nightmare",
+ "paradise",
+ "resurrections",
+ "operator",
+ "holoplay",
+ "throwback",
+ "updated",
+ "1999",
+ "2003",
+ "2021",
];
const App = () => {
- const [version, setVersion] = React.useState(versions[0]);
- // const [number, setNumber] = React.useState(0);
- const onButtonClick = () => {
- setVersion((s) => {
- const newVersion = versions[idx];
- idx = (idx + 1) % versions.length;
- console.log(newVersion);
- return newVersion;
- });
- };
- // const newNum = () => setNumber((n) => n + 1);
- console.log("version", version);
- // console.log("num", number);
+ const [version, setVersion] = React.useState(versions[0]);
+ // const [number, setNumber] = React.useState(0);
+ const onButtonClick = () => {
+ setVersion((s) => {
+ const newVersion = versions[idx];
+ idx = (idx + 1) % versions.length;
+ console.log(newVersion);
+ return newVersion;
+ });
+ };
+ // const newNum = () => setNumber((n) => n + 1);
+ console.log("version", version);
+ // console.log("num", number);
- return (
-
-
Rain
-
- {/* */}
-
-
- );
+ return (
+
+
Rain
+
+ {/* */}
+
+
+ );
};
root.render();
diff --git a/js/main.js b/js/main.js
index 3f6809b..5001a9f 100644
--- a/js/main.js
+++ b/js/main.js
@@ -7,7 +7,11 @@ document.addEventListener("touchmove", (e) => e.preventDefault(), {
});
const supportsWebGPU = async () => {
- return window.GPUQueue != null && navigator.gpu != null && navigator.gpu.getPreferredCanvasFormat != null;
+ return (
+ window.GPUQueue != null &&
+ navigator.gpu != null &&
+ navigator.gpu.getPreferredCanvasFormat != null
+ );
};
const isRunningSwiftShader = () => {
diff --git a/js/regl/bloomPass.js b/js/regl/bloomPass.js
index a2c09d8..d56a335 100644
--- a/js/regl/bloomPass.js
+++ b/js/regl/bloomPass.js
@@ -1,7 +1,7 @@
import { makePassFBO, makePass } from "./utils";
-import highPassFrag from '../../shaders/glsl/bloomPass.highPass.frag.glsl';
-import blurFrag from '../../shaders/glsl/bloomPass.blur.frag.glsl';
-import combineFrag from '../../shaders/glsl/bloomPass.combine.frag.glsl';
+import highPassFrag from "../../shaders/glsl/bloomPass.highPass.frag.glsl";
+import blurFrag from "../../shaders/glsl/bloomPass.blur.frag.glsl";
+import combineFrag from "../../shaders/glsl/bloomPass.combine.frag.glsl";
// The bloom pass is basically an added high-pass blur.
// The blur approximation is the sum of a pyramid of downscaled, blurred textures.
@@ -16,7 +16,9 @@ const makePyramid = (regl, height, halfFloat) =>
.map((_) => makePassFBO(regl, halfFloat));
const resizePyramid = (pyramid, vw, vh, scale) =>
- pyramid.forEach((fbo, index) => fbo.resize(Math.floor((vw * scale) / 2 ** index), Math.floor((vh * scale) / 2 ** index)));
+ pyramid.forEach((fbo, index) =>
+ fbo.resize(Math.floor((vw * scale) / 2 ** index), Math.floor((vh * scale) / 2 ** index)),
+ );
export default ({ regl, config }, inputs) => {
const { bloomStrength, bloomSize, highPassThreshold } = config;
@@ -94,12 +96,16 @@ export default ({ regl, config }, inputs) => {
const highPassFBO = highPassPyramid[i];
const hBlurFBO = hBlurPyramid[i];
const vBlurFBO = vBlurPyramid[i];
- highPass({ fbo: highPassFBO, frag: highPassFrag, tex: i === 0 ? inputs.primary : highPassPyramid[i - 1] });
+ highPass({
+ fbo: highPassFBO,
+ frag: highPassFrag,
+ tex: i === 0 ? inputs.primary : highPassPyramid[i - 1],
+ });
blur({ fbo: hBlurFBO, frag: blurFrag, tex: highPassFBO, direction: [1, 0] });
blur({ fbo: vBlurFBO, frag: blurFrag, tex: hBlurFBO, direction: [0, 1] });
}
combine({ frag: combineFrag });
- }
+ },
);
};
diff --git a/js/regl/imagePass.js b/js/regl/imagePass.js
index d12b017..f66dcd0 100644
--- a/js/regl/imagePass.js
+++ b/js/regl/imagePass.js
@@ -3,7 +3,8 @@ import imagePassFrag from "../../shaders/glsl/imagePass.frag.glsl";
// Multiplies the rendered rain and bloom by a loaded in image
-const defaultBGURL = "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0a/Flammarion_Colored.jpg/917px-Flammarion_Colored.jpg";
+const defaultBGURL =
+ "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0a/Flammarion_Colored.jpg/917px-Flammarion_Colored.jpg";
export default ({ regl, config }, inputs) => {
const output = makePassFBO(regl, config.useHalfFloat);
@@ -28,6 +29,6 @@ export default ({ regl, config }, inputs) => {
if (shouldRender) {
render({ frag: imagePassFrag });
}
- }
+ },
);
};
diff --git a/js/regl/lkgHelper.js b/js/regl/lkgHelper.js
index 98d5ecc..874b336 100644
--- a/js/regl/lkgHelper.js
+++ b/js/regl/lkgHelper.js
@@ -2,100 +2,95 @@
const HoloPlayCore = require("holoplay-core");
const recordedDevice = {
- buttons: [0, 0, 0, 0],
- calibration: {
- DPI: { value: 324 },
- center: { value: 0.15018756687641144 },
- configVersion: "3.0",
- flipImageX: { value: 0 },
- flipImageY: { value: 0 },
- flipSubp: { value: 0 },
- fringe: { value: 0 },
- invView: { value: 1 },
- pitch: { value: 52.58013153076172 },
- screenH: { value: 2048 },
- screenW: { value: 1536 },
- slope: { value: -7.145165920257568 },
- verticalAngle: { value: 0 },
- viewCone: { value: 40 },
- },
- defaultQuilt: {
- quiltAspect: 0.75,
- quiltX: 3840,
- quiltY: 3840,
- tileX: 8,
- tileY: 6,
- },
- hardwareVersion: "portrait",
- hwid: "LKG-P11063",
- index: 0,
- joystickIndex: -1,
- state: "ok",
- unityIndex: 1,
- windowCoords: [1440, 900],
+ buttons: [0, 0, 0, 0],
+ calibration: {
+ DPI: { value: 324 },
+ center: { value: 0.15018756687641144 },
+ configVersion: "3.0",
+ flipImageX: { value: 0 },
+ flipImageY: { value: 0 },
+ flipSubp: { value: 0 },
+ fringe: { value: 0 },
+ invView: { value: 1 },
+ pitch: { value: 52.58013153076172 },
+ screenH: { value: 2048 },
+ screenW: { value: 1536 },
+ slope: { value: -7.145165920257568 },
+ verticalAngle: { value: 0 },
+ viewCone: { value: 40 },
+ },
+ defaultQuilt: {
+ quiltAspect: 0.75,
+ quiltX: 3840,
+ quiltY: 3840,
+ tileX: 8,
+ tileY: 6,
+ },
+ hardwareVersion: "portrait",
+ hwid: "LKG-P11063",
+ index: 0,
+ joystickIndex: -1,
+ state: "ok",
+ unityIndex: 1,
+ windowCoords: [1440, 900],
};
const interpretDevice = (device) => {
- if (device == null) {
- return { enabled: false, tileX: 1, tileY: 1 };
- }
+ if (device == null) {
+ return { enabled: false, tileX: 1, tileY: 1 };
+ }
- const fov = 15;
+ const fov = 15;
- const calibration = Object.fromEntries(
- Object.entries(device.calibration)
- .map(([key, value]) => [key, value.value])
- .filter(([key, value]) => value != null)
- );
+ const calibration = Object.fromEntries(
+ Object.entries(device.calibration)
+ .map(([key, value]) => [key, value.value])
+ .filter(([key, value]) => value != null),
+ );
- const screenInches = calibration.screenW / calibration.DPI;
- const pitch =
- calibration.pitch *
- screenInches *
- Math.cos(Math.atan(1.0 / calibration.slope));
- const tilt =
- (calibration.screenH / (calibration.screenW * calibration.slope)) *
- -(calibration.flipImageX * 2 - 1);
- const subp = 1 / (calibration.screenW * 3);
+ const screenInches = calibration.screenW / calibration.DPI;
+ const pitch = calibration.pitch * screenInches * Math.cos(Math.atan(1.0 / calibration.slope));
+ const tilt =
+ (calibration.screenH / (calibration.screenW * calibration.slope)) *
+ -(calibration.flipImageX * 2 - 1);
+ const subp = 1 / (calibration.screenW * 3);
- const defaultQuilt = device.defaultQuilt;
+ const defaultQuilt = device.defaultQuilt;
- const quiltViewPortion = [
- (Math.floor(defaultQuilt.quiltX / defaultQuilt.tileX) *
- defaultQuilt.tileX) /
- defaultQuilt.quiltX,
- (Math.floor(defaultQuilt.quiltY / defaultQuilt.tileY) *
- defaultQuilt.tileY) /
- defaultQuilt.quiltY,
- ];
+ const quiltViewPortion = [
+ (Math.floor(defaultQuilt.quiltX / defaultQuilt.tileX) * defaultQuilt.tileX) /
+ defaultQuilt.quiltX,
+ (Math.floor(defaultQuilt.quiltY / defaultQuilt.tileY) * defaultQuilt.tileY) /
+ defaultQuilt.quiltY,
+ ];
- return {
- ...defaultQuilt,
- ...calibration,
- pitch,
- tilt,
- subp,
+ return {
+ ...defaultQuilt,
+ ...calibration,
+ pitch,
+ tilt,
+ subp,
- quiltViewPortion,
- fov,
- enabled: true,
- };
+ quiltViewPortion,
+ fov,
+ enabled: true,
+ };
};
export default async (useHoloplay = false, useRecordedDevice = false) => {
- if (!useHoloplay) {
- return interpretDevice(null);
- }
- // const HoloPlayCore = await import("../../lib/holoplaycore.module.js");
- const device = await new Promise(
- (resolve, reject) =>
- new HoloPlayCore.Client(
- (data) => resolve(data.devices?.[0]),
- (error) => resolve(null)
- )
- );
- if (device == null && useRecordedDevice) {
- return interpretDevice(recordedDevice);
- }
- return interpretDevice(device);
+ if (!useHoloplay) {
+ return interpretDevice(null);
+ }
+ // const HoloPlayCore = await import("../../lib/holoplaycore.module.js");
+ const device = await new Promise(
+ (resolve, reject) =>
+ new HoloPlayCore.Client(
+ (data) => resolve(data.devices?.[0]),
+ (error) => resolve(null),
+ ),
+ );
+ if (device == null && useRecordedDevice) {
+ return interpretDevice(recordedDevice);
+ }
+ return interpretDevice(device);
};
diff --git a/js/regl/main.js b/js/regl/main.js
index fb3cc55..3e7ef6d 100644
--- a/js/regl/main.js
+++ b/js/regl/main.js
@@ -7,24 +7,20 @@ import makeStripePass from "./stripePass.js";
import makeImagePass from "./imagePass.js";
import makeQuiltPass from "./quiltPass.js";
import makeMirrorPass from "./mirrorPass.js";
-import {
- setupCamera,
- cameraCanvas,
- cameraAspectRatio,
-} from "../utils/camera.js";
+import { setupCamera, cameraCanvas, cameraAspectRatio } from "../utils/camera.js";
import getLKG from "./lkgHelper.js";
const effects = {
- none: null,
- plain: makePalettePass,
- palette: makePalettePass,
- customStripes: makeStripePass,
- stripes: makeStripePass,
- pride: makeStripePass,
- transPride: makeStripePass,
- trans: makeStripePass,
- image: makeImagePass,
- mirror: makeMirrorPass,
+ none: null,
+ plain: makePalettePass,
+ palette: makePalettePass,
+ customStripes: makeStripePass,
+ stripes: makeStripePass,
+ pride: makeStripePass,
+ transPride: makeStripePass,
+ trans: makeStripePass,
+ image: makeImagePass,
+ mirror: makeMirrorPass,
};
const dimensions = { width: 1, height: 1 };
@@ -41,132 +37,124 @@ const dimensions = { width: 1, height: 1 };
// Promise.all([loadJS("lib/regl.min.js"), loadJS("lib/gl-matrix.js")]);
export const createRain = async (canvas, config, gl) => {
- const resize = () => {
- const dpr = window.devicePixelRatio || 1;
- canvas.width = Math.ceil(window.innerWidth * dpr * config.resolution);
- canvas.height = Math.ceil(window.innerHeight * dpr * config.resolution);
- };
+ const resize = () => {
+ const dpr = window.devicePixelRatio || 1;
+ canvas.width = Math.ceil(window.innerWidth * dpr * config.resolution);
+ canvas.height = Math.ceil(window.innerHeight * dpr * config.resolution);
+ };
- window.onresize = resize;
- if (document.fullscreenEnabled || document.webkitFullscreenEnabled) {
- window.ondblclick = () => {
- if (document.fullscreenElement == null) {
- if (canvas.webkitRequestFullscreen != null) {
- canvas.webkitRequestFullscreen();
- } else {
- canvas.requestFullscreen();
- }
- } else {
- document.exitFullscreen();
- }
- };
- }
- resize();
+ window.onresize = resize;
+ if (document.fullscreenEnabled || document.webkitFullscreenEnabled) {
+ window.ondblclick = () => {
+ if (document.fullscreenElement == null) {
+ if (canvas.webkitRequestFullscreen != null) {
+ canvas.webkitRequestFullscreen();
+ } else {
+ canvas.requestFullscreen();
+ }
+ } else {
+ document.exitFullscreen();
+ }
+ };
+ }
+ resize();
- if (config.useCamera) {
- await setupCamera();
- }
+ if (config.useCamera) {
+ await setupCamera();
+ }
- const extensions = [
- "OES_texture_half_float",
- "OES_texture_half_float_linear",
- ];
- // These extensions are also needed, but Safari misreports that they are missing
- const optionalExtensions = [
- "EXT_color_buffer_half_float",
- "WEBGL_color_buffer_float",
- "OES_standard_derivatives",
- ];
+ const extensions = ["OES_texture_half_float", "OES_texture_half_float_linear"];
+ // These extensions are also needed, but Safari misreports that they are missing
+ const optionalExtensions = [
+ "EXT_color_buffer_half_float",
+ "WEBGL_color_buffer_float",
+ "OES_standard_derivatives",
+ ];
- switch (config.testFix) {
- case "fwidth_10_1_2022_A":
- extensions.push("OES_standard_derivatives");
- break;
- case "fwidth_10_1_2022_B":
- optionalExtensions.forEach((ext) => extensions.push(ext));
- extensions.length = 0;
- break;
- }
+ switch (config.testFix) {
+ case "fwidth_10_1_2022_A":
+ extensions.push("OES_standard_derivatives");
+ break;
+ case "fwidth_10_1_2022_B":
+ optionalExtensions.forEach((ext) => extensions.push(ext));
+ extensions.length = 0;
+ break;
+ }
- const regl = createREGL({
- gl,
- pixelRatio: 1,
- extensions,
- optionalExtensions,
- });
+ const regl = createREGL({
+ gl,
+ pixelRatio: 1,
+ extensions,
+ optionalExtensions,
+ });
- const cameraTex = regl.texture(cameraCanvas);
- const lkg = await getLKG(config.useHoloplay, true);
+ const cameraTex = regl.texture(cameraCanvas);
+ const lkg = await getLKG(config.useHoloplay, true);
- // All this takes place in a full screen quad.
- const fullScreenQuad = makeFullScreenQuad(regl);
- const effectName = config.effect in effects ? config.effect : "palette";
- const context = { regl, config, lkg, cameraTex, cameraAspectRatio };
- const pipeline = makePipeline(context, [
- makeRain,
- makeBloomPass,
- effects[effectName],
- makeQuiltPass,
- ]);
+ // All this takes place in a full screen quad.
+ const fullScreenQuad = makeFullScreenQuad(regl);
+ const effectName = config.effect in effects ? config.effect : "palette";
+ const context = { regl, config, lkg, cameraTex, cameraAspectRatio };
+ const pipeline = makePipeline(context, [
+ makeRain,
+ makeBloomPass,
+ effects[effectName],
+ makeQuiltPass,
+ ]);
- const screenUniforms = { tex: pipeline[pipeline.length - 1].outputs.primary };
- const drawToScreen = regl({ uniforms: screenUniforms });
- await Promise.all(pipeline.map((step) => step.ready));
- pipeline.forEach((step) => step.setSize(canvas.width, canvas.height));
- dimensions.width = canvas.width;
- dimensions.height = canvas.height;
+ const screenUniforms = { tex: pipeline[pipeline.length - 1].outputs.primary };
+ const drawToScreen = regl({ uniforms: screenUniforms });
+ await Promise.all(pipeline.map((step) => step.ready));
+ pipeline.forEach((step) => step.setSize(canvas.width, canvas.height));
+ dimensions.width = canvas.width;
+ dimensions.height = canvas.height;
- const targetFrameTimeMilliseconds = 1000 / config.fps;
- let last = NaN;
+ const targetFrameTimeMilliseconds = 1000 / config.fps;
+ let last = NaN;
- const tick = regl.frame(({ viewportWidth, viewportHeight }) => {
- if (config.once) {
- tick.cancel();
- }
+ const tick = regl.frame(({ viewportWidth, viewportHeight }) => {
+ if (config.once) {
+ tick.cancel();
+ }
- const now = regl.now() * 1000;
+ const now = regl.now() * 1000;
- if (isNaN(last)) {
- last = now;
- }
+ if (isNaN(last)) {
+ last = now;
+ }
- const shouldRender =
- config.fps >= 60 ||
- now - last >= targetFrameTimeMilliseconds ||
- config.once == true;
+ const shouldRender =
+ config.fps >= 60 || now - last >= targetFrameTimeMilliseconds || config.once == true;
- if (shouldRender) {
- while (now - targetFrameTimeMilliseconds > last) {
- last += targetFrameTimeMilliseconds;
- }
- }
+ if (shouldRender) {
+ while (now - targetFrameTimeMilliseconds > last) {
+ last += targetFrameTimeMilliseconds;
+ }
+ }
- if (config.useCamera) {
- cameraTex(cameraCanvas);
- }
- if (
- dimensions.width !== viewportWidth ||
- dimensions.height !== viewportHeight
- ) {
- dimensions.width = viewportWidth;
- dimensions.height = viewportHeight;
- for (const step of pipeline) {
- step.setSize(viewportWidth, viewportHeight);
- }
- }
- fullScreenQuad(() => {
- for (const step of pipeline) {
- step.execute(shouldRender);
- }
- drawToScreen();
- });
- });
+ if (config.useCamera) {
+ cameraTex(cameraCanvas);
+ }
+ if (dimensions.width !== viewportWidth || dimensions.height !== viewportHeight) {
+ dimensions.width = viewportWidth;
+ dimensions.height = viewportHeight;
+ for (const step of pipeline) {
+ step.setSize(viewportWidth, viewportHeight);
+ }
+ }
+ fullScreenQuad(() => {
+ for (const step of pipeline) {
+ step.execute(shouldRender);
+ }
+ drawToScreen();
+ });
+ });
- return { regl, tick, canvas };
+ return { regl, tick, canvas };
};
export const destroyRain = ({ regl, tick, canvas }) => {
- tick.cancel(); // stop RAF
- regl.destroy(); // release all GPU resources & event listeners
- //canvas.remove(); // drop from the DOM
+ tick.cancel(); // stop RAF
+ regl.destroy(); // release all GPU resources & event listeners
+ //canvas.remove(); // drop from the DOM
};
diff --git a/js/regl/mirrorPass.js b/js/regl/mirrorPass.js
index dc52114..f225825 100644
--- a/js/regl/mirrorPass.js
+++ b/js/regl/mirrorPass.js
@@ -45,6 +45,6 @@ export default ({ regl, config, cameraTex, cameraAspectRatio }, inputs) => {
if (shouldRender) {
render({ frag: mirrorPassFrag });
}
- }
+ },
);
};
diff --git a/js/regl/palettePass.js b/js/regl/palettePass.js
index ed87a55..228e8bb 100644
--- a/js/regl/palettePass.js
+++ b/js/regl/palettePass.js
@@ -8,47 +8,45 @@ import palettePassFrag from "../../shaders/glsl/palettePass.frag.glsl";
// This shader introduces noise into the renders, to avoid banding
const makePalette = (regl, entries) => {
- const PALETTE_SIZE = 2048;
- const paletteColors = Array(PALETTE_SIZE);
+ const PALETTE_SIZE = 2048;
+ const paletteColors = Array(PALETTE_SIZE);
- // Convert HSL gradient into sorted RGB gradient, capping the ends
- const sortedEntries = entries
- .slice()
- .sort((e1, e2) => e1.at - e2.at)
- .map((entry) => ({
- rgb: colorToRGB(entry.color),
- arrayIndex: Math.floor(
- Math.max(Math.min(1, entry.at), 0) * (PALETTE_SIZE - 1)
- ),
- }));
- sortedEntries.unshift({ rgb: sortedEntries[0].rgb, arrayIndex: 0 });
- sortedEntries.push({
- rgb: sortedEntries[sortedEntries.length - 1].rgb,
- arrayIndex: PALETTE_SIZE - 1,
- });
+ // Convert HSL gradient into sorted RGB gradient, capping the ends
+ const sortedEntries = entries
+ .slice()
+ .sort((e1, e2) => e1.at - e2.at)
+ .map((entry) => ({
+ rgb: colorToRGB(entry.color),
+ arrayIndex: Math.floor(Math.max(Math.min(1, entry.at), 0) * (PALETTE_SIZE - 1)),
+ }));
+ sortedEntries.unshift({ rgb: sortedEntries[0].rgb, arrayIndex: 0 });
+ sortedEntries.push({
+ rgb: sortedEntries[sortedEntries.length - 1].rgb,
+ arrayIndex: PALETTE_SIZE - 1,
+ });
- // Interpolate between the sorted RGB entries to generate
- // the palette texture data
- sortedEntries.forEach((entry, index) => {
- paletteColors[entry.arrayIndex] = entry.rgb.slice();
- if (index + 1 < sortedEntries.length) {
- const nextEntry = sortedEntries[index + 1];
- const diff = nextEntry.arrayIndex - entry.arrayIndex;
- for (let i = 0; i < diff; i++) {
- const ratio = i / diff;
- paletteColors[entry.arrayIndex + i] = [
- entry.rgb[0] * (1 - ratio) + nextEntry.rgb[0] * ratio,
- entry.rgb[1] * (1 - ratio) + nextEntry.rgb[1] * ratio,
- entry.rgb[2] * (1 - ratio) + nextEntry.rgb[2] * ratio,
- ];
- }
- }
- });
+ // Interpolate between the sorted RGB entries to generate
+ // the palette texture data
+ sortedEntries.forEach((entry, index) => {
+ paletteColors[entry.arrayIndex] = entry.rgb.slice();
+ if (index + 1 < sortedEntries.length) {
+ const nextEntry = sortedEntries[index + 1];
+ const diff = nextEntry.arrayIndex - entry.arrayIndex;
+ for (let i = 0; i < diff; i++) {
+ const ratio = i / diff;
+ paletteColors[entry.arrayIndex + i] = [
+ entry.rgb[0] * (1 - ratio) + nextEntry.rgb[0] * ratio,
+ entry.rgb[1] * (1 - ratio) + nextEntry.rgb[1] * ratio,
+ entry.rgb[2] * (1 - ratio) + nextEntry.rgb[2] * ratio,
+ ];
+ }
+ }
+ });
- return make1DTexture(
- regl,
- paletteColors.map((rgb) => [...rgb, 1])
- );
+ return make1DTexture(
+ regl,
+ paletteColors.map((rgb) => [...rgb, 1]),
+ );
};
// The rendered texture's values are mapped to colors in a palette texture.
@@ -58,44 +56,44 @@ const makePalette = (regl, entries) => {
// in screen space.
export default ({ regl, config }, inputs) => {
- const output = makePassFBO(regl, config.useHalfFloat);
- const paletteTex = makePalette(regl, config.palette);
- const {
- backgroundColor,
- cursorColor,
- glintColor,
- cursorIntensity,
- glintIntensity,
- ditherMagnitude,
- } = config;
+ const output = makePassFBO(regl, config.useHalfFloat);
+ const paletteTex = makePalette(regl, config.palette);
+ const {
+ backgroundColor,
+ cursorColor,
+ glintColor,
+ cursorIntensity,
+ glintIntensity,
+ ditherMagnitude,
+ } = config;
- const render = regl({
- frag: regl.prop("frag"),
+ const render = regl({
+ frag: regl.prop("frag"),
- uniforms: {
- backgroundColor: colorToRGB(backgroundColor),
- cursorColor: colorToRGB(cursorColor),
- glintColor: colorToRGB(glintColor),
- cursorIntensity,
- glintIntensity,
- ditherMagnitude,
- tex: inputs.primary,
- bloomTex: inputs.bloom,
- paletteTex,
- },
- framebuffer: output,
- });
+ uniforms: {
+ backgroundColor: colorToRGB(backgroundColor),
+ cursorColor: colorToRGB(cursorColor),
+ glintColor: colorToRGB(glintColor),
+ cursorIntensity,
+ glintIntensity,
+ ditherMagnitude,
+ tex: inputs.primary,
+ bloomTex: inputs.bloom,
+ paletteTex,
+ },
+ framebuffer: output,
+ });
- return makePass(
- {
- primary: output,
- },
- palettePassFrag.loaded,
- (w, h) => output.resize(w, h),
- (shouldRender) => {
- if (shouldRender) {
- render({ frag: palettePassFrag });
- }
- }
- );
+ return makePass(
+ {
+ primary: output,
+ },
+ palettePassFrag.loaded,
+ (w, h) => output.resize(w, h),
+ (shouldRender) => {
+ if (shouldRender) {
+ render({ frag: palettePassFrag });
+ }
+ },
+ );
};
diff --git a/js/regl/quiltPass.js b/js/regl/quiltPass.js
index 917c120..7897a48 100644
--- a/js/regl/quiltPass.js
+++ b/js/regl/quiltPass.js
@@ -30,6 +30,6 @@ export default ({ regl, config, lkg }, inputs) => {
if (shouldRender) {
render({ frag: quiltPassFrag });
}
- }
+ },
);
};
diff --git a/js/regl/rainPass.js b/js/regl/rainPass.js
index e44950b..a41dc10 100644
--- a/js/regl/rainPass.js
+++ b/js/regl/rainPass.js
@@ -1,9 +1,4 @@
-import {
- loadImage,
- makePassFBO,
- makeDoubleBuffer,
- makePass,
-} from "./utils.js";
+import { loadImage, makePassFBO, makeDoubleBuffer, makePass } from "./utils.js";
import { mat4, vec3 } from "gl-matrix";
import rainPassIntro from "../../shaders/glsl/rainPass.intro.frag.glsl";
import rainPassRaindrop from "../../shaders/glsl/rainPass.raindrop.frag.glsl";
@@ -13,13 +8,11 @@ import rainPassVert from "../../shaders/glsl/rainPass.vert.glsl";
import rainPassFrag from "../../shaders/glsl/rainPass.frag.glsl";
const extractEntries = (src, keys) =>
- Object.fromEntries(
- Array.from(Object.entries(src)).filter(([key]) => keys.includes(key))
- );
+ Object.fromEntries(Array.from(Object.entries(src)).filter(([key]) => keys.includes(key)));
const rippleTypes = {
- box: 0,
- circle: 1,
+ box: 0,
+ circle: 1,
};
// These compute buffers are used to compute the properties of cells in the grid.
@@ -30,12 +23,12 @@ const rippleTypes = {
// These double buffers are smaller than the screen, because their pixels correspond
// with cells in the grid, and the cells' glyphs are much larger than a pixel.
const makeComputeDoubleBuffer = (regl, height, width) =>
- makeDoubleBuffer(regl, {
- width,
- height,
- wrapT: "clamp",
- type: "half float",
- });
+ makeDoubleBuffer(regl, {
+ width,
+ height,
+ wrapT: "clamp",
+ type: "half float",
+ });
const numVerticesPerQuad = 2 * 3;
const tlVert = [0, 0];
@@ -45,352 +38,301 @@ const brVert = [1, 1];
const quadVertices = [tlVert, trVert, brVert, tlVert, brVert, blVert];
export default ({ regl, config, lkg }) => {
- // The volumetric mode multiplies the number of columns
- // to reach the desired density, and then overlaps them
- const volumetric = config.volumetric;
- const density = volumetric && config.effect !== "none" ? config.density : 1;
- const [numRows, numColumns] = [
- config.numColumns,
- Math.floor(config.numColumns * density),
- ];
+ // The volumetric mode multiplies the number of columns
+ // to reach the desired density, and then overlaps them
+ const volumetric = config.volumetric;
+ const density = volumetric && config.effect !== "none" ? config.density : 1;
+ const [numRows, numColumns] = [config.numColumns, Math.floor(config.numColumns * density)];
- // The volumetric mode requires us to create a grid of quads,
- // rather than a single quad for our geometry
- const [numQuadRows, numQuadColumns] = volumetric
- ? [numRows, numColumns]
- : [1, 1];
- const numQuads = numQuadRows * numQuadColumns;
- const quadSize = [1 / numQuadColumns, 1 / numQuadRows];
+ // The volumetric mode requires us to create a grid of quads,
+ // rather than a single quad for our geometry
+ const [numQuadRows, numQuadColumns] = volumetric ? [numRows, numColumns] : [1, 1];
+ const numQuads = numQuadRows * numQuadColumns;
+ const quadSize = [1 / numQuadColumns, 1 / numQuadRows];
- // Various effect-related values
- const rippleType =
- config.rippleTypeName in rippleTypes
- ? rippleTypes[config.rippleTypeName]
- : -1;
- const slantVec = [Math.cos(config.slant), Math.sin(config.slant)];
- const slantScale =
- 1 / (Math.abs(Math.sin(2 * config.slant)) * (Math.sqrt(2) - 1) + 1);
- const showDebugView = config.effect === "none";
+ // Various effect-related values
+ const rippleType = config.rippleTypeName in rippleTypes ? rippleTypes[config.rippleTypeName] : -1;
+ const slantVec = [Math.cos(config.slant), Math.sin(config.slant)];
+ const slantScale = 1 / (Math.abs(Math.sin(2 * config.slant)) * (Math.sqrt(2) - 1) + 1);
+ const showDebugView = config.effect === "none";
- const commonUniforms = {
- ...extractEntries(config, [
- "animationSpeed",
- "glyphHeightToWidth",
- "glyphSequenceLength",
- "glyphTextureGridSize",
- ]),
- numColumns,
- numRows,
- showDebugView,
- };
+ const commonUniforms = {
+ ...extractEntries(config, [
+ "animationSpeed",
+ "glyphHeightToWidth",
+ "glyphSequenceLength",
+ "glyphTextureGridSize",
+ ]),
+ numColumns,
+ numRows,
+ showDebugView,
+ };
- const introDoubleBuffer = makeComputeDoubleBuffer(regl, 1, numColumns);
+ const introDoubleBuffer = makeComputeDoubleBuffer(regl, 1, numColumns);
- const introUniforms = {
- ...commonUniforms,
- ...extractEntries(config, ["fallSpeed", "skipIntro"]),
- };
- const intro = regl({
- frag: regl.prop("frag"),
- uniforms: {
- ...introUniforms,
- previousIntroState: introDoubleBuffer.back,
- },
+ const introUniforms = {
+ ...commonUniforms,
+ ...extractEntries(config, ["fallSpeed", "skipIntro"]),
+ };
+ const intro = regl({
+ frag: regl.prop("frag"),
+ uniforms: {
+ ...introUniforms,
+ previousIntroState: introDoubleBuffer.back,
+ },
- framebuffer: introDoubleBuffer.front,
- });
+ framebuffer: introDoubleBuffer.front,
+ });
- const raindropDoubleBuffer = makeComputeDoubleBuffer(
- regl,
- numRows,
- numColumns
- );
-
- const raindropUniforms = {
- ...commonUniforms,
- ...extractEntries(config, [
- "brightnessDecay",
- "fallSpeed",
- "raindropLength",
- "loops",
- "skipIntro",
- ]),
- };
- const raindrop = regl({
- frag: regl.prop("frag"),
- uniforms: {
- ...raindropUniforms,
- introState: introDoubleBuffer.front,
- previousRaindropState: raindropDoubleBuffer.back,
- },
+ const raindropDoubleBuffer = makeComputeDoubleBuffer(regl, numRows, numColumns);
- framebuffer: raindropDoubleBuffer.front,
- });
+ const raindropUniforms = {
+ ...commonUniforms,
+ ...extractEntries(config, [
+ "brightnessDecay",
+ "fallSpeed",
+ "raindropLength",
+ "loops",
+ "skipIntro",
+ ]),
+ };
+ const raindrop = regl({
+ frag: regl.prop("frag"),
+ uniforms: {
+ ...raindropUniforms,
+ introState: introDoubleBuffer.front,
+ previousRaindropState: raindropDoubleBuffer.back,
+ },
- const symbolDoubleBuffer = makeComputeDoubleBuffer(regl, numRows, numColumns);
+ framebuffer: raindropDoubleBuffer.front,
+ });
- const symbolUniforms = {
- ...commonUniforms,
- ...extractEntries(config, ["cycleSpeed", "cycleFrameSkip", "loops"]),
- };
- const symbol = regl({
- frag: regl.prop("frag"),
- uniforms: {
- ...symbolUniforms,
- raindropState: raindropDoubleBuffer.front,
- previousSymbolState: symbolDoubleBuffer.back,
- },
+ const symbolDoubleBuffer = makeComputeDoubleBuffer(regl, numRows, numColumns);
- framebuffer: symbolDoubleBuffer.front,
- });
+ const symbolUniforms = {
+ ...commonUniforms,
+ ...extractEntries(config, ["cycleSpeed", "cycleFrameSkip", "loops"]),
+ };
+ const symbol = regl({
+ frag: regl.prop("frag"),
+ uniforms: {
+ ...symbolUniforms,
+ raindropState: raindropDoubleBuffer.front,
+ previousSymbolState: symbolDoubleBuffer.back,
+ },
- const effectDoubleBuffer = makeComputeDoubleBuffer(regl, numRows, numColumns);
+ framebuffer: symbolDoubleBuffer.front,
+ });
- const effectUniforms = {
- ...commonUniforms,
- ...extractEntries(config, [
- "hasThunder",
- "rippleScale",
- "rippleSpeed",
- "rippleThickness",
- "loops",
- ]),
- rippleType,
- };
- const effect = regl({
- frag: regl.prop("frag"),
- uniforms: {
- ...effectUniforms,
- raindropState: raindropDoubleBuffer.front,
- previousEffectState: effectDoubleBuffer.back,
- },
+ const effectDoubleBuffer = makeComputeDoubleBuffer(regl, numRows, numColumns);
- framebuffer: effectDoubleBuffer.front,
- });
+ const effectUniforms = {
+ ...commonUniforms,
+ ...extractEntries(config, [
+ "hasThunder",
+ "rippleScale",
+ "rippleSpeed",
+ "rippleThickness",
+ "loops",
+ ]),
+ rippleType,
+ };
+ const effect = regl({
+ frag: regl.prop("frag"),
+ uniforms: {
+ ...effectUniforms,
+ raindropState: raindropDoubleBuffer.front,
+ previousEffectState: effectDoubleBuffer.back,
+ },
- const quadPositions = Array(numQuadRows)
- .fill()
- .map((_, y) =>
- Array(numQuadColumns)
- .fill()
- .map((_, x) => Array(numVerticesPerQuad).fill([x, y]))
- );
+ framebuffer: effectDoubleBuffer.front,
+ });
- // We render the code into an FBO using MSDFs: https://github.com/Chlumsky/msdfgen
- const glyphMSDF = loadImage(regl, config.glyphMSDFURL);
- const glintMSDF = loadImage(regl, config.glintMSDFURL);
- const baseTexture = loadImage(regl, config.baseTextureURL, true);
- const glintTexture = loadImage(regl, config.glintTextureURL, true);
- const output = makePassFBO(regl, config.useHalfFloat);
- const renderUniforms = {
- ...commonUniforms,
- ...extractEntries(config, [
- // vertex
- "forwardSpeed",
- "glyphVerticalSpacing",
- // fragment
- "baseBrightness",
- "baseContrast",
- "glintBrightness",
- "glintContrast",
- "hasBaseTexture",
- "hasGlintTexture",
- "brightnessThreshold",
- "brightnessOverride",
- "isolateCursor",
- "isolateGlint",
- "glyphEdgeCrop",
- "isPolar",
- ]),
- density,
- numQuadColumns,
- numQuadRows,
- quadSize,
- slantScale,
- slantVec,
- volumetric,
- };
- const render = regl({
- blend: {
- enable: true,
- func: {
- src: "one",
- dst: "one",
- },
- },
- vert: regl.prop("vert"),
- frag: regl.prop("frag"),
+ const quadPositions = Array(numQuadRows)
+ .fill()
+ .map((_, y) =>
+ Array(numQuadColumns)
+ .fill()
+ .map((_, x) => Array(numVerticesPerQuad).fill([x, y])),
+ );
- uniforms: {
- ...renderUniforms,
+ // We render the code into an FBO using MSDFs: https://github.com/Chlumsky/msdfgen
+ const glyphMSDF = loadImage(regl, config.glyphMSDFURL);
+ const glintMSDF = loadImage(regl, config.glintMSDFURL);
+ const baseTexture = loadImage(regl, config.baseTextureURL, true);
+ const glintTexture = loadImage(regl, config.glintTextureURL, true);
+ const output = makePassFBO(regl, config.useHalfFloat);
+ const renderUniforms = {
+ ...commonUniforms,
+ ...extractEntries(config, [
+ // vertex
+ "forwardSpeed",
+ "glyphVerticalSpacing",
+ // fragment
+ "baseBrightness",
+ "baseContrast",
+ "glintBrightness",
+ "glintContrast",
+ "hasBaseTexture",
+ "hasGlintTexture",
+ "brightnessThreshold",
+ "brightnessOverride",
+ "isolateCursor",
+ "isolateGlint",
+ "glyphEdgeCrop",
+ "isPolar",
+ ]),
+ density,
+ numQuadColumns,
+ numQuadRows,
+ quadSize,
+ slantScale,
+ slantVec,
+ volumetric,
+ };
+ const render = regl({
+ blend: {
+ enable: true,
+ func: {
+ src: "one",
+ dst: "one",
+ },
+ },
+ vert: regl.prop("vert"),
+ frag: regl.prop("frag"),
- raindropState: raindropDoubleBuffer.front,
- symbolState: symbolDoubleBuffer.front,
- effectState: effectDoubleBuffer.front,
- glyphMSDF: glyphMSDF.texture,
- glintMSDF: glintMSDF.texture,
- baseTexture: baseTexture.texture,
- glintTexture: glintTexture.texture,
- glyphTransform: regl.prop('glyphTransform'),
+ uniforms: {
+ ...renderUniforms,
- msdfPxRange: 4.0,
- glyphMSDFSize: () => [glyphMSDF.width(), glyphMSDF.height()],
- glintMSDFSize: () => [glintMSDF.width(), glintMSDF.height()],
+ raindropState: raindropDoubleBuffer.front,
+ symbolState: symbolDoubleBuffer.front,
+ effectState: effectDoubleBuffer.front,
+ glyphMSDF: glyphMSDF.texture,
+ glintMSDF: glintMSDF.texture,
+ baseTexture: baseTexture.texture,
+ glintTexture: glintTexture.texture,
+ glyphTransform: regl.prop("glyphTransform"),
- camera: regl.prop("camera"),
- transform: regl.prop("transform"),
- screenSize: regl.prop("screenSize"),
- },
+ msdfPxRange: 4.0,
+ glyphMSDFSize: () => [glyphMSDF.width(), glyphMSDF.height()],
+ glintMSDFSize: () => [glintMSDF.width(), glintMSDF.height()],
- viewport: regl.prop("viewport"),
+ camera: regl.prop("camera"),
+ transform: regl.prop("transform"),
+ screenSize: regl.prop("screenSize"),
+ },
- attributes: {
- aPosition: quadPositions,
- aCorner: Array(numQuads).fill(quadVertices),
- },
- count: numQuads * numVerticesPerQuad,
+ viewport: regl.prop("viewport"),
- framebuffer: output,
- });
+ attributes: {
+ aPosition: quadPositions,
+ aCorner: Array(numQuads).fill(quadVertices),
+ },
+ count: numQuads * numVerticesPerQuad,
- // Camera and transform math for the volumetric mode
- const screenSize = [1, 1];
- //const { mat4, vec3 } = glMatrix;
- const transform = mat4.create();
- if (volumetric && config.isometric) {
- mat4.rotateX(transform, transform, (Math.PI * 1) / 8);
- mat4.rotateY(transform, transform, (Math.PI * 1) / 4);
- mat4.translate(transform, transform, vec3.fromValues(0, 0, -1));
- mat4.scale(transform, transform, vec3.fromValues(1, 1, 2));
- } else if (lkg.enabled) {
- mat4.translate(transform, transform, vec3.fromValues(0, 0, -1.1));
- mat4.scale(transform, transform, vec3.fromValues(1, 1, 1));
- mat4.scale(transform, transform, vec3.fromValues(0.15, 0.15, 0.15));
- } else {
- mat4.translate(transform, transform, vec3.fromValues(0, 0, -1));
- }
- const camera = mat4.create();
+ framebuffer: output,
+ });
- const vantagePoints = [];
+ // Camera and transform math for the volumetric mode
+ const screenSize = [1, 1];
+ //const { mat4, vec3 } = glMatrix;
+ const transform = mat4.create();
+ if (volumetric && config.isometric) {
+ mat4.rotateX(transform, transform, (Math.PI * 1) / 8);
+ mat4.rotateY(transform, transform, (Math.PI * 1) / 4);
+ mat4.translate(transform, transform, vec3.fromValues(0, 0, -1));
+ mat4.scale(transform, transform, vec3.fromValues(1, 1, 2));
+ } else if (lkg.enabled) {
+ mat4.translate(transform, transform, vec3.fromValues(0, 0, -1.1));
+ mat4.scale(transform, transform, vec3.fromValues(1, 1, 1));
+ mat4.scale(transform, transform, vec3.fromValues(0.15, 0.15, 0.15));
+ } else {
+ mat4.translate(transform, transform, vec3.fromValues(0, 0, -1));
+ }
+ const camera = mat4.create();
- return makePass(
- {
- primary: output,
- },
- Promise.all([
- glyphMSDF.loaded,
- glintMSDF.loaded,
- baseTexture.loaded,
- glintTexture.loaded,
- ]),
- (w, h) => {
- output.resize(w, h);
- const aspectRatio = w / h;
+ const vantagePoints = [];
- const [numTileColumns, numTileRows] = [lkg.tileX, lkg.tileY];
- const numVantagePoints = numTileRows * numTileColumns;
- const tileWidth = Math.floor(w / numTileColumns);
- const tileHeight = Math.floor(h / numTileRows);
- vantagePoints.length = 0;
- for (let row = 0; row < numTileRows; row++) {
- for (let column = 0; column < numTileColumns; column++) {
- const index = column + row * numTileColumns;
- const camera = mat4.create();
+ return makePass(
+ {
+ primary: output,
+ },
+ Promise.all([glyphMSDF.loaded, glintMSDF.loaded, baseTexture.loaded, glintTexture.loaded]),
+ (w, h) => {
+ output.resize(w, h);
+ const aspectRatio = w / h;
- if (volumetric && config.isometric) {
- if (aspectRatio > 1) {
- mat4.ortho(
- camera,
- -1.5 * aspectRatio,
- 1.5 * aspectRatio,
- -1.5,
- 1.5,
- -1000,
- 1000
- );
- } else {
- mat4.ortho(
- camera,
- -1.5,
- 1.5,
- -1.5 / aspectRatio,
- 1.5 / aspectRatio,
- -1000,
- 1000
- );
- }
- } else if (lkg.enabled) {
- mat4.perspective(
- camera,
- (Math.PI / 180) * lkg.fov,
- lkg.quiltAspect,
- 0.0001,
- 1000
- );
+ const [numTileColumns, numTileRows] = [lkg.tileX, lkg.tileY];
+ const numVantagePoints = numTileRows * numTileColumns;
+ const tileWidth = Math.floor(w / numTileColumns);
+ const tileHeight = Math.floor(h / numTileRows);
+ vantagePoints.length = 0;
+ for (let row = 0; row < numTileRows; row++) {
+ for (let column = 0; column < numTileColumns; column++) {
+ const index = column + row * numTileColumns;
+ const camera = mat4.create();
- const distanceToTarget = -1; // TODO: Get from somewhere else
- let vantagePointAngle =
- (Math.PI / 180) *
- lkg.viewCone *
- (index / (numVantagePoints - 1) - 0.5);
- if (isNaN(vantagePointAngle)) {
- vantagePointAngle = 0;
- }
- const xOffset = distanceToTarget * Math.tan(vantagePointAngle);
+ if (volumetric && config.isometric) {
+ if (aspectRatio > 1) {
+ mat4.ortho(camera, -1.5 * aspectRatio, 1.5 * aspectRatio, -1.5, 1.5, -1000, 1000);
+ } else {
+ mat4.ortho(camera, -1.5, 1.5, -1.5 / aspectRatio, 1.5 / aspectRatio, -1000, 1000);
+ }
+ } else if (lkg.enabled) {
+ mat4.perspective(camera, (Math.PI / 180) * lkg.fov, lkg.quiltAspect, 0.0001, 1000);
- mat4.translate(camera, camera, vec3.fromValues(xOffset, 0, 0));
+ const distanceToTarget = -1; // TODO: Get from somewhere else
+ let vantagePointAngle =
+ (Math.PI / 180) * lkg.viewCone * (index / (numVantagePoints - 1) - 0.5);
+ if (isNaN(vantagePointAngle)) {
+ vantagePointAngle = 0;
+ }
+ const xOffset = distanceToTarget * Math.tan(vantagePointAngle);
- camera[8] =
- -xOffset /
- (distanceToTarget *
- Math.tan((Math.PI / 180) * 0.5 * lkg.fov) *
- lkg.quiltAspect); // Is this right??
- } else {
- mat4.perspective(
- camera,
- (Math.PI / 180) * 90,
- aspectRatio,
- 0.0001,
- 1000
- );
- }
+ mat4.translate(camera, camera, vec3.fromValues(xOffset, 0, 0));
- const viewport = {
- x: column * tileWidth,
- y: row * tileHeight,
- width: tileWidth,
- height: tileHeight,
- };
- vantagePoints.push({ camera, viewport });
- }
- }
- [screenSize[0], screenSize[1]] =
- aspectRatio > 1 ? [1, aspectRatio] : [1 / aspectRatio, 1];
- },
- (shouldRender) => {
- intro({ frag: rainPassIntro });
- raindrop({ frag: rainPassRaindrop });
- symbol({ frag: rainPassSymbol });
- effect({ frag: rainPassEffect });
+ camera[8] =
+ -xOffset /
+ (distanceToTarget * Math.tan((Math.PI / 180) * 0.5 * lkg.fov) * lkg.quiltAspect); // Is this right??
+ } else {
+ mat4.perspective(camera, (Math.PI / 180) * 90, aspectRatio, 0.0001, 1000);
+ }
- if (shouldRender) {
- regl.clear({
- depth: 1,
- color: [0, 0, 0, 1],
- framebuffer: output,
- });
-
- for (const vantagePoint of vantagePoints) {
- render({
- ...vantagePoint,
- transform,
- screenSize,
- vert: rainPassVert,
- frag: rainPassFrag,
- glyphTransform: [1, 0, 0, 1]
- });
- }
- }
- }
- );
+ const viewport = {
+ x: column * tileWidth,
+ y: row * tileHeight,
+ width: tileWidth,
+ height: tileHeight,
+ };
+ vantagePoints.push({ camera, viewport });
+ }
+ }
+ [screenSize[0], screenSize[1]] = aspectRatio > 1 ? [1, aspectRatio] : [1 / aspectRatio, 1];
+ },
+ (shouldRender) => {
+ intro({ frag: rainPassIntro });
+ raindrop({ frag: rainPassRaindrop });
+ symbol({ frag: rainPassSymbol });
+ effect({ frag: rainPassEffect });
+
+ if (shouldRender) {
+ regl.clear({
+ depth: 1,
+ color: [0, 0, 0, 1],
+ framebuffer: output,
+ });
+
+ for (const vantagePoint of vantagePoints) {
+ render({
+ ...vantagePoint,
+ transform,
+ screenSize,
+ vert: rainPassVert,
+ frag: rainPassFrag,
+ glyphTransform: [1, 0, 0, 1],
+ });
+ }
+ }
+ },
+ );
};
diff --git a/js/regl/stripePass.js b/js/regl/stripePass.js
index f962a17..ebde1e7 100644
--- a/js/regl/stripePass.js
+++ b/js/regl/stripePass.js
@@ -8,77 +8,77 @@ import stripePassFrag from "../../shaders/glsl/stripePass.frag.glsl";
// This shader introduces noise into the renders, to avoid banding
const transPrideStripeColors = [
- { space: "rgb", values: [0.36, 0.81, 0.98] },
- { space: "rgb", values: [0.96, 0.66, 0.72] },
- { space: "rgb", values: [1.0, 1.0, 1.0] },
- { space: "rgb", values: [0.96, 0.66, 0.72] },
- { space: "rgb", values: [0.36, 0.81, 0.98] },
+ { space: "rgb", values: [0.36, 0.81, 0.98] },
+ { space: "rgb", values: [0.96, 0.66, 0.72] },
+ { space: "rgb", values: [1.0, 1.0, 1.0] },
+ { space: "rgb", values: [0.96, 0.66, 0.72] },
+ { space: "rgb", values: [0.36, 0.81, 0.98] },
]
- .map((color) => Array(3).fill(color))
- .flat();
+ .map((color) => Array(3).fill(color))
+ .flat();
const prideStripeColors = [
- { space: "rgb", values: [0.89, 0.01, 0.01] },
- { space: "rgb", values: [1.0, 0.55, 0.0] },
- { space: "rgb", values: [1.0, 0.93, 0.0] },
- { space: "rgb", values: [0.0, 0.5, 0.15] },
- { space: "rgb", values: [0.0, 0.3, 1.0] },
- { space: "rgb", values: [0.46, 0.03, 0.53] },
+ { space: "rgb", values: [0.89, 0.01, 0.01] },
+ { space: "rgb", values: [1.0, 0.55, 0.0] },
+ { space: "rgb", values: [1.0, 0.93, 0.0] },
+ { space: "rgb", values: [0.0, 0.5, 0.15] },
+ { space: "rgb", values: [0.0, 0.3, 1.0] },
+ { space: "rgb", values: [0.46, 0.03, 0.53] },
]
- .map((color) => Array(2).fill(color))
- .flat();
+ .map((color) => Array(2).fill(color))
+ .flat();
export default ({ regl, config }, inputs) => {
- const output = makePassFBO(regl, config.useHalfFloat);
+ const output = makePassFBO(regl, config.useHalfFloat);
- const {
- backgroundColor,
- cursorColor,
- glintColor,
- cursorIntensity,
- glintIntensity,
- ditherMagnitude,
- } = config;
+ const {
+ backgroundColor,
+ cursorColor,
+ glintColor,
+ cursorIntensity,
+ glintIntensity,
+ ditherMagnitude,
+ } = config;
- // Expand and convert stripe colors into 1D texture data
- const stripeColors =
- "stripeColors" in config
- ? config.stripeColors
- : config.effect === "pride"
- ? prideStripeColors
- : transPrideStripeColors;
- const stripeTex = make1DTexture(
- regl,
- stripeColors.map((color) => [...colorToRGB(color), 1])
- );
+ // Expand and convert stripe colors into 1D texture data
+ const stripeColors =
+ "stripeColors" in config
+ ? config.stripeColors
+ : config.effect === "pride"
+ ? prideStripeColors
+ : transPrideStripeColors;
+ const stripeTex = make1DTexture(
+ regl,
+ stripeColors.map((color) => [...colorToRGB(color), 1]),
+ );
- const render = regl({
- frag: regl.prop("frag"),
+ const render = regl({
+ frag: regl.prop("frag"),
- uniforms: {
- backgroundColor: colorToRGB(backgroundColor),
- cursorColor: colorToRGB(cursorColor),
- glintColor: colorToRGB(glintColor),
- cursorIntensity,
- glintIntensity,
- ditherMagnitude,
- tex: inputs.primary,
- bloomTex: inputs.bloom,
- stripeTex,
- },
- framebuffer: output,
- });
+ uniforms: {
+ backgroundColor: colorToRGB(backgroundColor),
+ cursorColor: colorToRGB(cursorColor),
+ glintColor: colorToRGB(glintColor),
+ cursorIntensity,
+ glintIntensity,
+ ditherMagnitude,
+ tex: inputs.primary,
+ bloomTex: inputs.bloom,
+ stripeTex,
+ },
+ framebuffer: output,
+ });
- return makePass(
- {
- primary: output,
- },
- null,
- (w, h) => output.resize(w, h),
- (shouldRender) => {
- if (shouldRender) {
- render({ frag: stripePassFrag });
- }
- }
- );
+ return makePass(
+ {
+ primary: output,
+ },
+ null,
+ (w, h) => output.resize(w, h),
+ (shouldRender) => {
+ if (shouldRender) {
+ render({ frag: stripePassFrag });
+ }
+ },
+ );
};
diff --git a/js/regl/utils.js b/js/regl/utils.js
index efbb284..5678acf 100644
--- a/js/regl/utils.js
+++ b/js/regl/utils.js
@@ -8,7 +8,8 @@ const makePassTexture = (regl, halfFloat) =>
mag: "linear",
});
-const makePassFBO = (regl, halfFloat) => regl.framebuffer({ color: makePassTexture(regl, halfFloat) });
+const makePassFBO = (regl, halfFloat) =>
+ regl.framebuffer({ color: makePassTexture(regl, halfFloat) });
const makeDoubleBuffer = (regl, props) => {
const state = Array(2)
@@ -17,7 +18,7 @@ const makeDoubleBuffer = (regl, props) => {
regl.framebuffer({
color: regl.texture(props),
depthStencil: false,
- })
+ }),
);
return {
front: ({ tick }) => state[tick % 2],
@@ -149,6 +150,21 @@ const makePass = (outputs, ready, setSize, execute) => ({
});
const makePipeline = (context, steps) =>
- steps.filter((f) => f != null).reduce((pipeline, f, i) => [...pipeline, f(context, i == 0 ? null : pipeline[i - 1].outputs)], []);
+ steps
+ .filter((f) => f != null)
+ .reduce(
+ (pipeline, f, i) => [...pipeline, f(context, i == 0 ? null : pipeline[i - 1].outputs)],
+ [],
+ );
-export { makePassTexture, makePassFBO, makeDoubleBuffer, loadImage, loadText, makeFullScreenQuad, make1DTexture, makePass, makePipeline };
+export {
+ makePassTexture,
+ makePassFBO,
+ makeDoubleBuffer,
+ loadImage,
+ loadText,
+ makeFullScreenQuad,
+ make1DTexture,
+ makePass,
+ makePipeline,
+};
diff --git a/js/utils/config.js b/js/utils/config.js
index e1c16fb..6df82ae 100644
--- a/js/utils/config.js
+++ b/js/utils/config.js
@@ -16,402 +16,402 @@ import texMesh from "../../assets/mesh.png";
import texMetal from "../../assets/metal.png";
const fonts = {
- coptic: {
- // The script the Gnostic codices were written in
- glyphMSDFURL: msdfCoptic,
- glyphSequenceLength: 32,
- glyphTextureGridSize: [8, 8],
- },
- gothic: {
- // The script the Codex Argenteus was written in
- glyphMSDFURL: msdfGothic,
- glyphSequenceLength: 27,
- glyphTextureGridSize: [8, 8],
- },
- matrixcode: {
- // The glyphs seen in the film trilogy
- glyphMSDFURL: msdfMatrixCode,
- glyphSequenceLength: 57,
- glyphTextureGridSize: [8, 8],
- },
- megacity: {
- // The glyphs seen in the film trilogy
- glyphMSDFURL: megacity,
- glyphSequenceLength: 64,
- glyphTextureGridSize: [8, 8],
- },
- resurrections: {
- // The glyphs seen in the film trilogy
- glyphMSDFURL: msdfRes,
- glintMSDFURL: msdfResGlint,
- glyphSequenceLength: 135,
- glyphTextureGridSize: [13, 12],
- },
- huberfishA: {
- glyphMSDFURL: msdfHuberfishA,
- glyphSequenceLength: 34,
- glyphTextureGridSize: [6, 6],
- },
- huberfishD: {
- glyphMSDFURL: msdfHuberfishD,
- glyphSequenceLength: 34,
- glyphTextureGridSize: [6, 6],
- },
- gtarg_tenretniolleh: {
- glyphMSDFURL: msdfGtargTenretni,
- glyphSequenceLength: 36,
- glyphTextureGridSize: [6, 6],
- },
- gtarg_alientext: {
- glyphMSDFURL: msdfGtargAlienText,
- glyphSequenceLength: 38,
- glyphTextureGridSize: [8, 5],
- },
- neomatrixology: {
- glyphMSDFURL: msdfNeoMatrixology,
- glyphSequenceLength: 12,
- glyphTextureGridSize: [4, 4],
- },
+ coptic: {
+ // The script the Gnostic codices were written in
+ glyphMSDFURL: msdfCoptic,
+ glyphSequenceLength: 32,
+ glyphTextureGridSize: [8, 8],
+ },
+ gothic: {
+ // The script the Codex Argenteus was written in
+ glyphMSDFURL: msdfGothic,
+ glyphSequenceLength: 27,
+ glyphTextureGridSize: [8, 8],
+ },
+ matrixcode: {
+ // The glyphs seen in the film trilogy
+ glyphMSDFURL: msdfMatrixCode,
+ glyphSequenceLength: 57,
+ glyphTextureGridSize: [8, 8],
+ },
+ megacity: {
+ // The glyphs seen in the film trilogy
+ glyphMSDFURL: megacity,
+ glyphSequenceLength: 64,
+ glyphTextureGridSize: [8, 8],
+ },
+ resurrections: {
+ // The glyphs seen in the film trilogy
+ glyphMSDFURL: msdfRes,
+ glintMSDFURL: msdfResGlint,
+ glyphSequenceLength: 135,
+ glyphTextureGridSize: [13, 12],
+ },
+ huberfishA: {
+ glyphMSDFURL: msdfHuberfishA,
+ glyphSequenceLength: 34,
+ glyphTextureGridSize: [6, 6],
+ },
+ huberfishD: {
+ glyphMSDFURL: msdfHuberfishD,
+ glyphSequenceLength: 34,
+ glyphTextureGridSize: [6, 6],
+ },
+ gtarg_tenretniolleh: {
+ glyphMSDFURL: msdfGtargTenretni,
+ glyphSequenceLength: 36,
+ glyphTextureGridSize: [6, 6],
+ },
+ gtarg_alientext: {
+ glyphMSDFURL: msdfGtargAlienText,
+ glyphSequenceLength: 38,
+ glyphTextureGridSize: [8, 5],
+ },
+ neomatrixology: {
+ glyphMSDFURL: msdfNeoMatrixology,
+ glyphSequenceLength: 12,
+ glyphTextureGridSize: [4, 4],
+ },
};
const textureURLs = {
- sand: texSand,
- pixels: texPixels,
- mesh: texMesh,
- metal: texMetal,
+ sand: texSand,
+ pixels: texPixels,
+ mesh: texMesh,
+ metal: texMetal,
};
const hsl = (...values) => ({ space: "hsl", values });
const rgb = (...values) => ({ space: "rgb", values });
const defaults = {
- font: "matrixcode",
- effect: "palette", // The name of the effect to apply at the end of the process— mainly handles coloration
- baseTexture: null, // The name of the texture to apply to the base layer of the glyphs
- glintTexture: null, // The name of the texture to apply to the glint layer of the glyphs
- useCamera: false,
- backgroundColor: hsl(0, 0, 0), // The color "behind" the glyphs
- isolateCursor: true, // Whether the "cursor"— the brightest glyph at the bottom of a raindrop— has its own color
- cursorColor: hsl(0.242, 1, 0.73), // The color of the cursor
- cursorIntensity: 2, // The intensity of the cursor
- isolateGlint: false, // Whether the "glint"— highlights on certain symbols in the font— should appear
- glintColor: hsl(0, 0, 1), // The color of the glint
- glintIntensity: 1, // The intensity of the glint
- volumetric: false, // A mode where the raindrops appear in perspective
- animationSpeed: 1, // The global rate that all animations progress
- fps: 60, // The target frame rate (frames per second) of the effect
- forwardSpeed: 0.25, // The speed volumetric rain approaches the eye
- bloomStrength: 0.7, // The intensity of the bloom
- bloomSize: 0.4, // The amount the bloom calculation is scaled
- highPassThreshold: 0.1, // The minimum brightness that is still blurred
- cycleSpeed: 0.03, // The speed glyphs change
- cycleFrameSkip: 1, // The global minimum number of frames between glyphs cycling
- baseBrightness: -0.5, // The brightness of the glyphs, before any effects are applied
- baseContrast: 1.1, // The contrast of the glyphs, before any effects are applied
- glintBrightness: -1.5, // The brightness of the glints, before any effects are applied
- glintContrast: 2.5, // The contrast of the glints, before any effects are applied
- brightnessOverride: 0.0, // A global override to the brightness of displayed glyphs. Only used if it is > 0.
- brightnessThreshold: 0, // The minimum brightness for a glyph to still be considered visible
- brightnessDecay: 1.0, // The rate at which glyphs light up and dim
- ditherMagnitude: 0.05, // The magnitude of the random per-pixel dimming
- fallSpeed: 0.3, // The speed the raindrops progress downwards
- glyphEdgeCrop: 0.0, // The border around a glyph in a font texture that should be cropped out
- glyphHeightToWidth: 1, // The aspect ratio of glyphs
- glyphVerticalSpacing: 1, // The ratio of the vertical distance between glyphs to their height
- glyphFlip: false, // Whether to horizontally reflect the glyphs
- glyphRotation: 0, // An angle to rotate the glyphs. Currently limited to 90° increments
- hasThunder: false, // An effect that adds dramatic lightning flashes
- isPolar: false, // Whether the glyphs arc across the screen or sit in a standard grid
- rippleTypeName: null, // The variety of the ripple effect
- rippleThickness: 0.2, // The thickness of the ripple effect
- rippleScale: 30, // The size of the ripple effect
- rippleSpeed: 0.2, // The rate at which the ripple effect progresses
- numColumns: 80, // The maximum dimension of the glyph grid
- density: 1, // In volumetric mode, the number of actual columns compared to the grid
- palette: [
- // The color palette that glyph brightness is color mapped to
- { color: hsl(0.3, 0.9, 0.0), at: 0.0 },
- { color: hsl(0.3, 0.9, 0.2), at: 0.2 },
- { color: hsl(0.3, 0.9, 0.7), at: 0.7 },
- { color: hsl(0.3, 0.9, 0.8), at: 0.8 },
- ],
- raindropLength: 0.75, // Adjusts the frequency of raindrops (and their length) in a column
- slant: 0, // The angle at which rain falls; the orientation of the glyph grid
- resolution: 0.75, // An overall scale multiplier
- useHalfFloat: false,
- renderer: "regl", // The preferred web graphics API
- suppressWarnings: false, // Whether to show warnings to visitors on load
- isometric: false,
- useHoloplay: false,
- loops: false,
- skipIntro: true,
- testFix: null,
+ font: "matrixcode",
+ effect: "palette", // The name of the effect to apply at the end of the process— mainly handles coloration
+ baseTexture: null, // The name of the texture to apply to the base layer of the glyphs
+ glintTexture: null, // The name of the texture to apply to the glint layer of the glyphs
+ useCamera: false,
+ backgroundColor: hsl(0, 0, 0), // The color "behind" the glyphs
+ isolateCursor: true, // Whether the "cursor"— the brightest glyph at the bottom of a raindrop— has its own color
+ cursorColor: hsl(0.242, 1, 0.73), // The color of the cursor
+ cursorIntensity: 2, // The intensity of the cursor
+ isolateGlint: false, // Whether the "glint"— highlights on certain symbols in the font— should appear
+ glintColor: hsl(0, 0, 1), // The color of the glint
+ glintIntensity: 1, // The intensity of the glint
+ volumetric: false, // A mode where the raindrops appear in perspective
+ animationSpeed: 1, // The global rate that all animations progress
+ fps: 60, // The target frame rate (frames per second) of the effect
+ forwardSpeed: 0.25, // The speed volumetric rain approaches the eye
+ bloomStrength: 0.7, // The intensity of the bloom
+ bloomSize: 0.4, // The amount the bloom calculation is scaled
+ highPassThreshold: 0.1, // The minimum brightness that is still blurred
+ cycleSpeed: 0.03, // The speed glyphs change
+ cycleFrameSkip: 1, // The global minimum number of frames between glyphs cycling
+ baseBrightness: -0.5, // The brightness of the glyphs, before any effects are applied
+ baseContrast: 1.1, // The contrast of the glyphs, before any effects are applied
+ glintBrightness: -1.5, // The brightness of the glints, before any effects are applied
+ glintContrast: 2.5, // The contrast of the glints, before any effects are applied
+ brightnessOverride: 0.0, // A global override to the brightness of displayed glyphs. Only used if it is > 0.
+ brightnessThreshold: 0, // The minimum brightness for a glyph to still be considered visible
+ brightnessDecay: 1.0, // The rate at which glyphs light up and dim
+ ditherMagnitude: 0.05, // The magnitude of the random per-pixel dimming
+ fallSpeed: 0.3, // The speed the raindrops progress downwards
+ glyphEdgeCrop: 0.0, // The border around a glyph in a font texture that should be cropped out
+ glyphHeightToWidth: 1, // The aspect ratio of glyphs
+ glyphVerticalSpacing: 1, // The ratio of the vertical distance between glyphs to their height
+ glyphFlip: false, // Whether to horizontally reflect the glyphs
+ glyphRotation: 0, // An angle to rotate the glyphs. Currently limited to 90° increments
+ hasThunder: false, // An effect that adds dramatic lightning flashes
+ isPolar: false, // Whether the glyphs arc across the screen or sit in a standard grid
+ rippleTypeName: null, // The variety of the ripple effect
+ rippleThickness: 0.2, // The thickness of the ripple effect
+ rippleScale: 30, // The size of the ripple effect
+ rippleSpeed: 0.2, // The rate at which the ripple effect progresses
+ numColumns: 80, // The maximum dimension of the glyph grid
+ density: 1, // In volumetric mode, the number of actual columns compared to the grid
+ palette: [
+ // The color palette that glyph brightness is color mapped to
+ { color: hsl(0.3, 0.9, 0.0), at: 0.0 },
+ { color: hsl(0.3, 0.9, 0.2), at: 0.2 },
+ { color: hsl(0.3, 0.9, 0.7), at: 0.7 },
+ { color: hsl(0.3, 0.9, 0.8), at: 0.8 },
+ ],
+ raindropLength: 0.75, // Adjusts the frequency of raindrops (and their length) in a column
+ slant: 0, // The angle at which rain falls; the orientation of the glyph grid
+ resolution: 0.75, // An overall scale multiplier
+ useHalfFloat: false,
+ renderer: "regl", // The preferred web graphics API
+ suppressWarnings: false, // Whether to show warnings to visitors on load
+ isometric: false,
+ useHoloplay: false,
+ loops: false,
+ skipIntro: true,
+ testFix: null,
};
const versions = {
- classic: {},
- megacity: {
- font: "megacity",
- animationSpeed: 0.5,
- numColumns: 40,
- },
- neomatrixology: {
- font: "neomatrixology",
- animationSpeed: 0.8,
- numColumns: 40,
- palette: [
- { color: hsl(0.15, 0.9, 0.0), at: 0.0 },
- { color: hsl(0.15, 0.9, 0.2), at: 0.2 },
- { color: hsl(0.15, 0.9, 0.7), at: 0.7 },
- { color: hsl(0.15, 0.9, 0.8), at: 0.8 },
- ],
- cursorColor: hsl(0.167, 1, 0.75),
- cursorIntensity: 2,
- },
- operator: {
- cursorColor: hsl(0.375, 1, 0.66),
- cursorIntensity: 3,
- bloomSize: 0.6,
- bloomStrength: 0.75,
- highPassThreshold: 0.0,
- cycleSpeed: 0.01,
- cycleFrameSkip: 8,
- brightnessOverride: 0.22,
- brightnessThreshold: 0,
- fallSpeed: 0.6,
- glyphEdgeCrop: 0.15,
- glyphHeightToWidth: 1.35,
- rippleTypeName: "box",
- numColumns: 108,
- palette: [
- { color: hsl(0.4, 0.8, 0.0), at: 0.0 },
- { color: hsl(0.4, 0.8, 0.5), at: 0.5 },
- { color: hsl(0.4, 0.8, 1.0), at: 1.0 },
- ],
- raindropLength: 1.5,
- },
- nightmare: {
- font: "gothic",
- isolateCursor: false,
- highPassThreshold: 0.7,
- baseBrightness: -0.8,
- brightnessDecay: 0.75,
- fallSpeed: 1.2,
- hasThunder: true,
- numColumns: 60,
- cycleSpeed: 0.35,
- palette: [
- { color: hsl(0.0, 1.0, 0.0), at: 0.0 },
- { color: hsl(0.0, 1.0, 0.2), at: 0.2 },
- { color: hsl(0.0, 1.0, 0.4), at: 0.4 },
- { color: hsl(0.1, 1.0, 0.7), at: 0.7 },
- { color: hsl(0.2, 1.0, 1.0), at: 1.0 },
- ],
- raindropLength: 0.5,
- slant: (22.5 * Math.PI) / 180,
- },
- paradise: {
- font: "coptic",
- isolateCursor: false,
- bloomStrength: 1,
- highPassThreshold: 0,
- cycleSpeed: 0.005,
- baseBrightness: -1.3,
- baseContrast: 2,
- brightnessDecay: 0.05,
- fallSpeed: 0.02,
- isPolar: true,
- rippleTypeName: "circle",
- rippleSpeed: 0.1,
- numColumns: 40,
- palette: [
- { color: hsl(0.0, 0.0, 0.0), at: 0.0 },
- { color: hsl(0.0, 0.8, 0.3), at: 0.3 },
- { color: hsl(0.1, 0.8, 0.5), at: 0.5 },
- { color: hsl(0.1, 1.0, 0.6), at: 0.6 },
- { color: hsl(0.1, 1.0, 0.9), at: 0.9 },
- ],
- raindropLength: 0.4,
- },
- resurrections: {
- font: "resurrections",
- glyphEdgeCrop: 0.1,
- cursorColor: hsl(0.292, 1, 0.8),
- cursorIntensity: 2,
- baseBrightness: -0.7,
- baseContrast: 1.17,
- highPassThreshold: 0,
- numColumns: 70,
- cycleSpeed: 0.03,
- bloomStrength: 0.7,
- fallSpeed: 0.3,
- palette: [
- { color: hsl(0.375, 0.9, 0.0), at: 0.0 },
- { color: hsl(0.375, 1.0, 0.6), at: 0.92 },
- { color: hsl(0.375, 1.0, 1.0), at: 1.0 },
- ],
- },
- trinity: {
- font: "resurrections",
- glintTexture: "metal",
- baseTexture: "pixels",
- glyphEdgeCrop: 0.1,
- cursorColor: hsl(0.292, 1, 0.8),
- cursorIntensity: 2,
- isolateGlint: true,
- glintColor: hsl(0.131, 1, 0.6),
- glintIntensity: 3,
- glintBrightness: -0.5,
- glintContrast: 1.5,
- baseBrightness: -0.4,
- baseContrast: 1.5,
- highPassThreshold: 0,
- numColumns: 60,
- cycleSpeed: 0.03,
- bloomStrength: 0.7,
- fallSpeed: 0.3,
- palette: [
- { color: hsl(0.37, 0.6, 0.0), at: 0.0 },
- { color: hsl(0.37, 0.6, 0.5), at: 1.0 },
- ],
- cycleSpeed: 0.01,
- volumetric: true,
- forwardSpeed: 0.2,
- raindropLength: 0.3,
- density: 0.75,
- },
- morpheus: {
- font: "resurrections",
- glintTexture: "mesh",
- baseTexture: "metal",
- glyphEdgeCrop: 0.1,
- cursorColor: hsl(0.333, 1, 0.85),
- cursorIntensity: 2,
- isolateGlint: true,
- glintColor: hsl(0.4, 1, 0.5),
- glintIntensity: 2,
- glintBrightness: -1.5,
- glintContrast: 3,
- baseBrightness: -0.3,
- baseContrast: 1.5,
- highPassThreshold: 0,
- numColumns: 60,
- cycleSpeed: 0.03,
- bloomStrength: 0.7,
- fallSpeed: 0.3,
- palette: [
- { color: hsl(0.97, 0.6, 0.0), at: 0.0 },
- { color: hsl(0.97, 0.6, 0.5), at: 1.0 },
- ],
- cycleSpeed: 0.015,
- volumetric: true,
- forwardSpeed: 0.1,
- raindropLength: 0.4,
- density: 0.75,
- },
- bugs: {
- font: "resurrections",
- glintTexture: "sand",
- baseTexture: "metal",
- glyphEdgeCrop: 0.1,
- cursorColor: hsl(0.619, 1, 0.65),
- cursorIntensity: 2,
- isolateGlint: true,
- glintColor: hsl(0.625, 1, 0.6),
- glintIntensity: 3,
- glintBrightness: -1,
- glintContrast: 3,
- baseBrightness: -0.3,
- baseContrast: 1.5,
- highPassThreshold: 0,
- numColumns: 60,
- cycleSpeed: 0.03,
- bloomStrength: 0.7,
- fallSpeed: 0.3,
- palette: [
- { color: hsl(0.12, 0.6, 0.0), at: 0.0 },
- { color: hsl(0.14, 0.6, 0.5), at: 1.0 },
- ],
- cycleSpeed: 0.01,
- volumetric: true,
- forwardSpeed: 0.4,
- raindropLength: 0.3,
- density: 0.75,
- },
- palimpsest: {
- font: "huberfishA",
- isolateCursor: false,
- bloomStrength: 0.2,
- numColumns: 40,
- raindropLength: 1.2,
- cycleFrameSkip: 3,
- fallSpeed: 0.5,
- slant: Math.PI * -0.0625,
- palette: [
- { color: hsl(0.15, 0.25, 0.9), at: 0.0 },
- { color: hsl(0.6, 0.8, 0.1), at: 0.4 },
- ],
- },
- twilight: {
- font: "huberfishD",
- cursorColor: hsl(0.167, 1, 0.8),
- cursorIntensity: 1.5,
- bloomStrength: 0.1,
- numColumns: 50,
- raindropLength: 0.9,
- fallSpeed: 0.1,
- highPassThreshold: 0.0,
- palette: [
- { color: hsl(0.6, 1.0, 0.05), at: 0.0 },
- { color: hsl(0.6, 0.8, 0.1), at: 0.1 },
- { color: hsl(0.88, 0.8, 0.5), at: 0.5 },
- { color: hsl(0.15, 1.0, 0.6), at: 0.8 },
- // { color: hsl(0.1, 1.0, 0.9), at: 1.0 },
- ],
- },
+ classic: {},
+ megacity: {
+ font: "megacity",
+ animationSpeed: 0.5,
+ numColumns: 40,
+ },
+ neomatrixology: {
+ font: "neomatrixology",
+ animationSpeed: 0.8,
+ numColumns: 40,
+ palette: [
+ { color: hsl(0.15, 0.9, 0.0), at: 0.0 },
+ { color: hsl(0.15, 0.9, 0.2), at: 0.2 },
+ { color: hsl(0.15, 0.9, 0.7), at: 0.7 },
+ { color: hsl(0.15, 0.9, 0.8), at: 0.8 },
+ ],
+ cursorColor: hsl(0.167, 1, 0.75),
+ cursorIntensity: 2,
+ },
+ operator: {
+ cursorColor: hsl(0.375, 1, 0.66),
+ cursorIntensity: 3,
+ bloomSize: 0.6,
+ bloomStrength: 0.75,
+ highPassThreshold: 0.0,
+ cycleSpeed: 0.01,
+ cycleFrameSkip: 8,
+ brightnessOverride: 0.22,
+ brightnessThreshold: 0,
+ fallSpeed: 0.6,
+ glyphEdgeCrop: 0.15,
+ glyphHeightToWidth: 1.35,
+ rippleTypeName: "box",
+ numColumns: 108,
+ palette: [
+ { color: hsl(0.4, 0.8, 0.0), at: 0.0 },
+ { color: hsl(0.4, 0.8, 0.5), at: 0.5 },
+ { color: hsl(0.4, 0.8, 1.0), at: 1.0 },
+ ],
+ raindropLength: 1.5,
+ },
+ nightmare: {
+ font: "gothic",
+ isolateCursor: false,
+ highPassThreshold: 0.7,
+ baseBrightness: -0.8,
+ brightnessDecay: 0.75,
+ fallSpeed: 1.2,
+ hasThunder: true,
+ numColumns: 60,
+ cycleSpeed: 0.35,
+ palette: [
+ { color: hsl(0.0, 1.0, 0.0), at: 0.0 },
+ { color: hsl(0.0, 1.0, 0.2), at: 0.2 },
+ { color: hsl(0.0, 1.0, 0.4), at: 0.4 },
+ { color: hsl(0.1, 1.0, 0.7), at: 0.7 },
+ { color: hsl(0.2, 1.0, 1.0), at: 1.0 },
+ ],
+ raindropLength: 0.5,
+ slant: (22.5 * Math.PI) / 180,
+ },
+ paradise: {
+ font: "coptic",
+ isolateCursor: false,
+ bloomStrength: 1,
+ highPassThreshold: 0,
+ cycleSpeed: 0.005,
+ baseBrightness: -1.3,
+ baseContrast: 2,
+ brightnessDecay: 0.05,
+ fallSpeed: 0.02,
+ isPolar: true,
+ rippleTypeName: "circle",
+ rippleSpeed: 0.1,
+ numColumns: 40,
+ palette: [
+ { color: hsl(0.0, 0.0, 0.0), at: 0.0 },
+ { color: hsl(0.0, 0.8, 0.3), at: 0.3 },
+ { color: hsl(0.1, 0.8, 0.5), at: 0.5 },
+ { color: hsl(0.1, 1.0, 0.6), at: 0.6 },
+ { color: hsl(0.1, 1.0, 0.9), at: 0.9 },
+ ],
+ raindropLength: 0.4,
+ },
+ resurrections: {
+ font: "resurrections",
+ glyphEdgeCrop: 0.1,
+ cursorColor: hsl(0.292, 1, 0.8),
+ cursorIntensity: 2,
+ baseBrightness: -0.7,
+ baseContrast: 1.17,
+ highPassThreshold: 0,
+ numColumns: 70,
+ cycleSpeed: 0.03,
+ bloomStrength: 0.7,
+ fallSpeed: 0.3,
+ palette: [
+ { color: hsl(0.375, 0.9, 0.0), at: 0.0 },
+ { color: hsl(0.375, 1.0, 0.6), at: 0.92 },
+ { color: hsl(0.375, 1.0, 1.0), at: 1.0 },
+ ],
+ },
+ trinity: {
+ font: "resurrections",
+ glintTexture: "metal",
+ baseTexture: "pixels",
+ glyphEdgeCrop: 0.1,
+ cursorColor: hsl(0.292, 1, 0.8),
+ cursorIntensity: 2,
+ isolateGlint: true,
+ glintColor: hsl(0.131, 1, 0.6),
+ glintIntensity: 3,
+ glintBrightness: -0.5,
+ glintContrast: 1.5,
+ baseBrightness: -0.4,
+ baseContrast: 1.5,
+ highPassThreshold: 0,
+ numColumns: 60,
+ cycleSpeed: 0.03,
+ bloomStrength: 0.7,
+ fallSpeed: 0.3,
+ palette: [
+ { color: hsl(0.37, 0.6, 0.0), at: 0.0 },
+ { color: hsl(0.37, 0.6, 0.5), at: 1.0 },
+ ],
+ cycleSpeed: 0.01,
+ volumetric: true,
+ forwardSpeed: 0.2,
+ raindropLength: 0.3,
+ density: 0.75,
+ },
+ morpheus: {
+ font: "resurrections",
+ glintTexture: "mesh",
+ baseTexture: "metal",
+ glyphEdgeCrop: 0.1,
+ cursorColor: hsl(0.333, 1, 0.85),
+ cursorIntensity: 2,
+ isolateGlint: true,
+ glintColor: hsl(0.4, 1, 0.5),
+ glintIntensity: 2,
+ glintBrightness: -1.5,
+ glintContrast: 3,
+ baseBrightness: -0.3,
+ baseContrast: 1.5,
+ highPassThreshold: 0,
+ numColumns: 60,
+ cycleSpeed: 0.03,
+ bloomStrength: 0.7,
+ fallSpeed: 0.3,
+ palette: [
+ { color: hsl(0.97, 0.6, 0.0), at: 0.0 },
+ { color: hsl(0.97, 0.6, 0.5), at: 1.0 },
+ ],
+ cycleSpeed: 0.015,
+ volumetric: true,
+ forwardSpeed: 0.1,
+ raindropLength: 0.4,
+ density: 0.75,
+ },
+ bugs: {
+ font: "resurrections",
+ glintTexture: "sand",
+ baseTexture: "metal",
+ glyphEdgeCrop: 0.1,
+ cursorColor: hsl(0.619, 1, 0.65),
+ cursorIntensity: 2,
+ isolateGlint: true,
+ glintColor: hsl(0.625, 1, 0.6),
+ glintIntensity: 3,
+ glintBrightness: -1,
+ glintContrast: 3,
+ baseBrightness: -0.3,
+ baseContrast: 1.5,
+ highPassThreshold: 0,
+ numColumns: 60,
+ cycleSpeed: 0.03,
+ bloomStrength: 0.7,
+ fallSpeed: 0.3,
+ palette: [
+ { color: hsl(0.12, 0.6, 0.0), at: 0.0 },
+ { color: hsl(0.14, 0.6, 0.5), at: 1.0 },
+ ],
+ cycleSpeed: 0.01,
+ volumetric: true,
+ forwardSpeed: 0.4,
+ raindropLength: 0.3,
+ density: 0.75,
+ },
+ palimpsest: {
+ font: "huberfishA",
+ isolateCursor: false,
+ bloomStrength: 0.2,
+ numColumns: 40,
+ raindropLength: 1.2,
+ cycleFrameSkip: 3,
+ fallSpeed: 0.5,
+ slant: Math.PI * -0.0625,
+ palette: [
+ { color: hsl(0.15, 0.25, 0.9), at: 0.0 },
+ { color: hsl(0.6, 0.8, 0.1), at: 0.4 },
+ ],
+ },
+ twilight: {
+ font: "huberfishD",
+ cursorColor: hsl(0.167, 1, 0.8),
+ cursorIntensity: 1.5,
+ bloomStrength: 0.1,
+ numColumns: 50,
+ raindropLength: 0.9,
+ fallSpeed: 0.1,
+ highPassThreshold: 0.0,
+ palette: [
+ { color: hsl(0.6, 1.0, 0.05), at: 0.0 },
+ { color: hsl(0.6, 0.8, 0.1), at: 0.1 },
+ { color: hsl(0.88, 0.8, 0.5), at: 0.5 },
+ { color: hsl(0.15, 1.0, 0.6), at: 0.8 },
+ // { color: hsl(0.1, 1.0, 0.9), at: 1.0 },
+ ],
+ },
- holoplay: {
- font: "resurrections",
- glintTexture: "metal",
- glyphEdgeCrop: 0.1,
- cursorColor: hsl(0.292, 1, 0.8),
- cursorIntensity: 2,
- isolateGlint: true,
- glintColor: hsl(0.131, 1, 0.6),
- glintIntensity: 3,
- glintBrightness: -0.5,
- glintContrast: 1.5,
- baseBrightness: -0.4,
- baseContrast: 1.5,
- highPassThreshold: 0,
- cycleSpeed: 0.03,
- bloomStrength: 0.7,
- fallSpeed: 0.3,
- palette: [
- { color: hsl(0.37, 0.6, 0.0), at: 0.0 },
- { color: hsl(0.37, 0.6, 0.5), at: 1.0 },
- ],
- cycleSpeed: 0.01,
- raindropLength: 0.3,
+ holoplay: {
+ font: "resurrections",
+ glintTexture: "metal",
+ glyphEdgeCrop: 0.1,
+ cursorColor: hsl(0.292, 1, 0.8),
+ cursorIntensity: 2,
+ isolateGlint: true,
+ glintColor: hsl(0.131, 1, 0.6),
+ glintIntensity: 3,
+ glintBrightness: -0.5,
+ glintContrast: 1.5,
+ baseBrightness: -0.4,
+ baseContrast: 1.5,
+ highPassThreshold: 0,
+ cycleSpeed: 0.03,
+ bloomStrength: 0.7,
+ fallSpeed: 0.3,
+ palette: [
+ { color: hsl(0.37, 0.6, 0.0), at: 0.0 },
+ { color: hsl(0.37, 0.6, 0.5), at: 1.0 },
+ ],
+ cycleSpeed: 0.01,
+ raindropLength: 0.3,
- renderer: "regl",
- numColumns: 20,
- ditherMagnitude: 0,
- bloomStrength: 0,
- volumetric: true,
- forwardSpeed: 0,
- density: 3,
- useHoloplay: true,
- },
+ renderer: "regl",
+ numColumns: 20,
+ ditherMagnitude: 0,
+ bloomStrength: 0,
+ volumetric: true,
+ forwardSpeed: 0,
+ density: 3,
+ useHoloplay: true,
+ },
- ["3d"]: {
- volumetric: true,
- fallSpeed: 0.5,
- cycleSpeed: 0.03,
- baseBrightness: -0.9,
- baseContrast: 1.5,
- raindropLength: 0.3,
- },
+ ["3d"]: {
+ volumetric: true,
+ fallSpeed: 0.5,
+ cycleSpeed: 0.03,
+ baseBrightness: -0.9,
+ baseContrast: 1.5,
+ raindropLength: 0.3,
+ },
};
versions.throwback = versions.operator;
versions.updated = versions.resurrections;
@@ -419,119 +419,118 @@ versions["1999"] = versions.operator;
versions["2003"] = versions.classic;
versions["2021"] = versions.resurrections;
-const range = (f, min = -Infinity, max = Infinity) =>
- Math.max(min, Math.min(max, f));
+const range = (f, min = -Infinity, max = Infinity) => Math.max(min, Math.min(max, f));
const nullNaN = (f) => (isNaN(f) ? null : f);
const isTrue = (s) => s.toLowerCase().includes("true");
const parseColor = (isHSL) => (s) => ({
- space: isHSL ? "hsl" : "rgb",
- values: s.split(",").map(parseFloat),
+ space: isHSL ? "hsl" : "rgb",
+ values: s.split(",").map(parseFloat),
});
const parseColors = (isHSL) => (s) => {
- const values = s.split(",").map(parseFloat);
- const space = isHSL ? "hsl" : "rgb";
- return Array(Math.floor(values.length / 3))
- .fill()
- .map((_, index) => ({
- space,
- values: values.slice(index * 3, (index + 1) * 3),
- }));
+ const values = s.split(",").map(parseFloat);
+ const space = isHSL ? "hsl" : "rgb";
+ return Array(Math.floor(values.length / 3))
+ .fill()
+ .map((_, index) => ({
+ space,
+ values: values.slice(index * 3, (index + 1) * 3),
+ }));
};
const parsePalette = (isHSL) => (s) => {
- const values = s.split(",").map(parseFloat);
- const space = isHSL ? "hsl" : "rgb";
- return Array(Math.floor(values.length / 4))
- .fill()
- .map((_, index) => {
- const colorValues = values.slice(index * 4, (index + 1) * 4);
- return {
- color: {
- space,
- values: colorValues.slice(0, 3),
- },
- at: colorValues[3],
- };
- });
+ const values = s.split(",").map(parseFloat);
+ const space = isHSL ? "hsl" : "rgb";
+ return Array(Math.floor(values.length / 4))
+ .fill()
+ .map((_, index) => {
+ const colorValues = values.slice(index * 4, (index + 1) * 4);
+ return {
+ color: {
+ space,
+ values: colorValues.slice(0, 3),
+ },
+ at: colorValues[3],
+ };
+ });
};
const paramMapping = {
- testFix: { key: "testFix", parser: (s) => s },
- version: { key: "version", parser: (s) => s },
- font: { key: "font", parser: (s) => s },
- effect: { key: "effect", parser: (s) => s },
- camera: { key: "useCamera", parser: isTrue },
- numColumns: { key: "numColumns", parser: (s) => nullNaN(parseInt(s)) },
- density: { key: "density", parser: (s) => nullNaN(range(parseFloat(s), 0)) },
- resolution: { key: "resolution", parser: (s) => nullNaN(parseFloat(s)) },
- animationSpeed: {
- key: "animationSpeed",
- parser: (s) => nullNaN(parseFloat(s)),
- },
- forwardSpeed: {
- key: "forwardSpeed",
- parser: (s) => nullNaN(parseFloat(s)),
- },
- cycleSpeed: { key: "cycleSpeed", parser: (s) => nullNaN(parseFloat(s)) },
- fallSpeed: { key: "fallSpeed", parser: (s) => nullNaN(parseFloat(s)) },
- raindropLength: {
- key: "raindropLength",
- parser: (s) => nullNaN(parseFloat(s)),
- },
- slant: {
- key: "slant",
- parser: (s) => nullNaN((parseFloat(s) * Math.PI) / 180),
- },
- bloomSize: {
- key: "bloomSize",
- parser: (s) => nullNaN(range(parseFloat(s), 0, 1)),
- },
- bloomStrength: {
- key: "bloomStrength",
- parser: (s) => nullNaN(range(parseFloat(s), 0, 1)),
- },
- ditherMagnitude: {
- key: "ditherMagnitude",
- parser: (s) => nullNaN(range(parseFloat(s), 0, 1)),
- },
- url: { key: "bgURL", parser: (s) => s },
- palette: { key: "palette", parser: parsePalette(false) },
- stripeColors: { key: "stripeColors", parser: parseColors(false) },
- backgroundColor: { key: "backgroundColor", parser: parseColor(false) },
- cursorColor: { key: "cursorColor", parser: parseColor(false) },
- glintColor: { key: "glintColor", parser: parseColor(false) },
+ testFix: { key: "testFix", parser: (s) => s },
+ version: { key: "version", parser: (s) => s },
+ font: { key: "font", parser: (s) => s },
+ effect: { key: "effect", parser: (s) => s },
+ camera: { key: "useCamera", parser: isTrue },
+ numColumns: { key: "numColumns", parser: (s) => nullNaN(parseInt(s)) },
+ density: { key: "density", parser: (s) => nullNaN(range(parseFloat(s), 0)) },
+ resolution: { key: "resolution", parser: (s) => nullNaN(parseFloat(s)) },
+ animationSpeed: {
+ key: "animationSpeed",
+ parser: (s) => nullNaN(parseFloat(s)),
+ },
+ forwardSpeed: {
+ key: "forwardSpeed",
+ parser: (s) => nullNaN(parseFloat(s)),
+ },
+ cycleSpeed: { key: "cycleSpeed", parser: (s) => nullNaN(parseFloat(s)) },
+ fallSpeed: { key: "fallSpeed", parser: (s) => nullNaN(parseFloat(s)) },
+ raindropLength: {
+ key: "raindropLength",
+ parser: (s) => nullNaN(parseFloat(s)),
+ },
+ slant: {
+ key: "slant",
+ parser: (s) => nullNaN((parseFloat(s) * Math.PI) / 180),
+ },
+ bloomSize: {
+ key: "bloomSize",
+ parser: (s) => nullNaN(range(parseFloat(s), 0, 1)),
+ },
+ bloomStrength: {
+ key: "bloomStrength",
+ parser: (s) => nullNaN(range(parseFloat(s), 0, 1)),
+ },
+ ditherMagnitude: {
+ key: "ditherMagnitude",
+ parser: (s) => nullNaN(range(parseFloat(s), 0, 1)),
+ },
+ url: { key: "bgURL", parser: (s) => s },
+ palette: { key: "palette", parser: parsePalette(false) },
+ stripeColors: { key: "stripeColors", parser: parseColors(false) },
+ backgroundColor: { key: "backgroundColor", parser: parseColor(false) },
+ cursorColor: { key: "cursorColor", parser: parseColor(false) },
+ glintColor: { key: "glintColor", parser: parseColor(false) },
- paletteHSL: { key: "palette", parser: parsePalette(true) },
- stripeHSL: { key: "stripeColors", parser: parseColors(true) },
- backgroundHSL: { key: "backgroundColor", parser: parseColor(true) },
- cursorHSL: { key: "cursorColor", parser: parseColor(true) },
- glintHSL: { key: "glintColor", parser: parseColor(true) },
+ paletteHSL: { key: "palette", parser: parsePalette(true) },
+ stripeHSL: { key: "stripeColors", parser: parseColors(true) },
+ backgroundHSL: { key: "backgroundColor", parser: parseColor(true) },
+ cursorHSL: { key: "cursorColor", parser: parseColor(true) },
+ glintHSL: { key: "glintColor", parser: parseColor(true) },
- cursorIntensity: {
- key: "cursorIntensity",
- parser: (s) => nullNaN(range(parseFloat(s), 0, Infinity)),
- },
+ cursorIntensity: {
+ key: "cursorIntensity",
+ parser: (s) => nullNaN(range(parseFloat(s), 0, Infinity)),
+ },
- glyphIntensity: {
- key: "glyphIntensity",
- parser: (s) => nullNaN(range(parseFloat(s), 0, Infinity)),
- },
+ glyphIntensity: {
+ key: "glyphIntensity",
+ parser: (s) => nullNaN(range(parseFloat(s), 0, Infinity)),
+ },
- volumetric: { key: "volumetric", parser: isTrue },
- glyphFlip: { key: "glyphFlip", parser: isTrue },
- glyphRotation: {
- key: "glyphRotation",
- parser: (s) => nullNaN(range(parseFloat(s), 0, Infinity)),
- },
- loops: { key: "loops", parser: isTrue },
- fps: { key: "fps", parser: (s) => nullNaN(range(parseFloat(s), 0, 60)) },
- skipIntro: { key: "skipIntro", parser: isTrue },
- renderer: { key: "renderer", parser: (s) => s },
- suppressWarnings: { key: "suppressWarnings", parser: isTrue },
- once: { key: "once", parser: isTrue },
- isometric: { key: "isometric", parser: isTrue },
+ volumetric: { key: "volumetric", parser: isTrue },
+ glyphFlip: { key: "glyphFlip", parser: isTrue },
+ glyphRotation: {
+ key: "glyphRotation",
+ parser: (s) => nullNaN(range(parseFloat(s), 0, Infinity)),
+ },
+ loops: { key: "loops", parser: isTrue },
+ fps: { key: "fps", parser: (s) => nullNaN(range(parseFloat(s), 0, 60)) },
+ skipIntro: { key: "skipIntro", parser: isTrue },
+ renderer: { key: "renderer", parser: (s) => s },
+ suppressWarnings: { key: "suppressWarnings", parser: isTrue },
+ once: { key: "once", parser: isTrue },
+ isometric: { key: "isometric", parser: isTrue },
};
paramMapping.paletteRGB = paramMapping.palette;
@@ -546,72 +545,57 @@ paramMapping.angle = paramMapping.slant;
paramMapping.colors = paramMapping.stripeColors;
export default (urlParams) => {
- const validParams = Object.fromEntries(
- Object.entries(urlParams)
- .filter(([key]) => key in paramMapping)
- .map(([key, value]) => [
- paramMapping[key].key,
- paramMapping[key].parser(value),
- ])
- .filter(([_, value]) => value != null)
- );
+ const validParams = Object.fromEntries(
+ Object.entries(urlParams)
+ .filter(([key]) => key in paramMapping)
+ .map(([key, value]) => [paramMapping[key].key, paramMapping[key].parser(value)])
+ .filter(([_, value]) => value != null),
+ );
- if (validParams.effect != null) {
- if (validParams.cursorColor == null) {
- validParams.cursorColor = hsl(0, 0, 1);
- }
+ if (validParams.effect != null) {
+ if (validParams.cursorColor == null) {
+ validParams.cursorColor = hsl(0, 0, 1);
+ }
- if (validParams.cursorIntensity == null) {
- validParams.cursorIntensity = 2;
- }
+ if (validParams.cursorIntensity == null) {
+ validParams.cursorIntensity = 2;
+ }
- if (validParams.glintColor == null) {
- validParams.glintColor = hsl(0, 0, 1);
- }
+ if (validParams.glintColor == null) {
+ validParams.glintColor = hsl(0, 0, 1);
+ }
- if (validParams.glyphIntensity == null) {
- validParams.glyphIntensity = 1;
- }
- }
+ if (validParams.glyphIntensity == null) {
+ validParams.glyphIntensity = 1;
+ }
+ }
- const version =
- validParams.version in versions
- ? versions[validParams.version]
- : versions.classic;
- const fontName = [validParams.font, version.font, defaults.font].find(
- (name) => name in fonts
- );
- const font = fonts[fontName];
+ const version =
+ validParams.version in versions ? versions[validParams.version] : versions.classic;
+ const fontName = [validParams.font, version.font, defaults.font].find((name) => name in fonts);
+ const font = fonts[fontName];
- const baseTextureURL =
- textureURLs[
- [version.baseTexture, defaults.baseTexture].find(
- (name) => name in textureURLs
- )
- ];
- const hasBaseTexture = baseTextureURL != null;
- const glintTextureURL =
- textureURLs[
- [version.glintTexture, defaults.glintTexture].find(
- (name) => name in textureURLs
- )
- ];
- const hasGlintTexture = glintTextureURL != null;
+ const baseTextureURL =
+ textureURLs[[version.baseTexture, defaults.baseTexture].find((name) => name in textureURLs)];
+ const hasBaseTexture = baseTextureURL != null;
+ const glintTextureURL =
+ textureURLs[[version.glintTexture, defaults.glintTexture].find((name) => name in textureURLs)];
+ const hasGlintTexture = glintTextureURL != null;
- const config = {
- ...defaults,
- ...version,
- ...font,
- ...validParams,
- baseTextureURL,
- glintTextureURL,
- hasBaseTexture,
- hasGlintTexture,
- };
+ const config = {
+ ...defaults,
+ ...version,
+ ...font,
+ ...validParams,
+ baseTextureURL,
+ glintTextureURL,
+ hasBaseTexture,
+ hasGlintTexture,
+ };
- if (config.bloomSize <= 0) {
- config.bloomStrength = 0;
- }
+ if (config.bloomSize <= 0) {
+ config.bloomStrength = 0;
+ }
- return config;
+ return config;
};
diff --git a/js/webgpu/bloomPass.js b/js/webgpu/bloomPass.js
index 78c3a4e..a0767fb 100644
--- a/js/webgpu/bloomPass.js
+++ b/js/webgpu/bloomPass.js
@@ -1,5 +1,11 @@
import { structs } from "../../lib/gpu-buffer.js";
-import { makeComputeTarget, loadShader, makeUniformBuffer, makeBindGroup, makePass } from "./utils.js";
+import {
+ makeComputeTarget,
+ loadShader,
+ makeUniformBuffer,
+ makeBindGroup,
+ makePass,
+} from "./utils.js";
// const makePyramid = makeComputeTarget;
@@ -20,8 +26,8 @@ const makePyramid = (device, size, pyramidHeight) =>
.map((_, index) =>
makeComputeTarget(
device,
- size.map((x) => Math.floor(x * 2 ** -index))
- )
+ size.map((x) => Math.floor(x * 2 ** -index)),
+ ),
);
const destroyPyramid = (pyramid) => pyramid?.forEach((texture) => texture.destroy());
@@ -47,7 +53,10 @@ export default ({ config, device }) => {
return makePass("No Bloom", null, (size, inputs) => ({ ...inputs, bloom: emptyTexture }));
}
- const assets = [loadShader(device, "shaders/wgsl/bloomBlur.wgsl"), loadShader(device, "shaders/wgsl/bloomCombine.wgsl")];
+ const assets = [
+ loadShader(device, "shaders/wgsl/bloomBlur.wgsl"),
+ loadShader(device, "shaders/wgsl/bloomCombine.wgsl"),
+ ];
const linearSampler = device.createSampler({
magFilter: "linear",
@@ -122,12 +131,27 @@ export default ({ config, device }) => {
for (let i = 0; i < pyramidHeight; i++) {
const hBlurPyramidView = makePyramidLevelView(hBlurPyramid, i);
const vBlurPyramidView = makePyramidLevelView(vBlurPyramid, i);
- hBlurBindGroups[i] = makeBindGroup(device, blurPipeline, 0, [hBlurBuffer, linearSampler, srcView, hBlurPyramidView]);
- vBlurBindGroups[i] = makeBindGroup(device, blurPipeline, 0, [vBlurBuffer, linearSampler, hBlurPyramidView, vBlurPyramidView]);
+ hBlurBindGroups[i] = makeBindGroup(device, blurPipeline, 0, [
+ hBlurBuffer,
+ linearSampler,
+ srcView,
+ hBlurPyramidView,
+ ]);
+ vBlurBindGroups[i] = makeBindGroup(device, blurPipeline, 0, [
+ vBlurBuffer,
+ linearSampler,
+ hBlurPyramidView,
+ vBlurPyramidView,
+ ]);
srcView = hBlurPyramidView;
}
- combineBindGroup = makeBindGroup(device, combinePipeline, 0, [combineBuffer, linearSampler, ...makePyramidViews(vBlurPyramid), output.createView()]);
+ combineBindGroup = makeBindGroup(device, combinePipeline, 0, [
+ combineBuffer,
+ linearSampler,
+ ...makePyramidViews(vBlurPyramid),
+ output.createView(),
+ ]);
return {
...inputs,
@@ -144,7 +168,11 @@ export default ({ config, device }) => {
computePass.setPipeline(blurPipeline);
for (let i = 0; i < pyramidHeight; i++) {
- const dispatchSize = [Math.ceil(Math.floor(scaledScreenSize[0] * 2 ** -i) / 32), Math.floor(Math.floor(scaledScreenSize[1] * 2 ** -i)), 1];
+ const dispatchSize = [
+ Math.ceil(Math.floor(scaledScreenSize[0] * 2 ** -i) / 32),
+ Math.floor(Math.floor(scaledScreenSize[1] * 2 ** -i)),
+ 1,
+ ];
computePass.setBindGroup(0, hBlurBindGroups[i]);
computePass.dispatchWorkgroups(...dispatchSize);
computePass.setBindGroup(0, vBlurBindGroups[i]);
diff --git a/js/webgpu/endPass.js b/js/webgpu/endPass.js
index 5030aad..0f25241 100644
--- a/js/webgpu/endPass.js
+++ b/js/webgpu/endPass.js
@@ -45,7 +45,10 @@ export default ({ device, canvasFormat, canvasContext }) => {
})();
const build = (size, inputs) => {
- renderBindGroup = makeBindGroup(device, renderPipeline, 0, [nearestSampler, inputs.primary.createView()]);
+ renderBindGroup = makeBindGroup(device, renderPipeline, 0, [
+ nearestSampler,
+ inputs.primary.createView(),
+ ]);
return null;
};
diff --git a/js/webgpu/imagePass.js b/js/webgpu/imagePass.js
index ee859de..1f21005 100644
--- a/js/webgpu/imagePass.js
+++ b/js/webgpu/imagePass.js
@@ -1,9 +1,17 @@
import { structs } from "../../lib/gpu-buffer.js";
-import { makeComputeTarget, makeUniformBuffer, loadTexture, loadShader, makeBindGroup, makePass } from "./utils.js";
+import {
+ makeComputeTarget,
+ makeUniformBuffer,
+ loadTexture,
+ loadShader,
+ makeBindGroup,
+ makePass,
+} from "./utils.js";
// Multiplies the rendered rain and bloom by a loaded in image
-const defaultBGURL = "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0a/Flammarion_Colored.jpg/917px-Flammarion_Colored.jpg";
+const defaultBGURL =
+ "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0a/Flammarion_Colored.jpg/917px-Flammarion_Colored.jpg";
export default ({ config, device }) => {
const bgURL = "bgURL" in config ? config.bgURL : defaultBGURL;
diff --git a/js/webgpu/main.js b/js/webgpu/main.js
index aa5ce42..43c2699 100644
--- a/js/webgpu/main.js
+++ b/js/webgpu/main.js
@@ -74,7 +74,10 @@ export default async (canvas, config) => {
const cameraTex = device.createTexture({
size: cameraSize,
format: "rgba8unorm",
- usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
+ usage:
+ GPUTextureUsage.TEXTURE_BINDING |
+ GPUTextureUsage.COPY_DST |
+ GPUTextureUsage.RENDER_ATTACHMENT,
});
const context = {
@@ -90,7 +93,12 @@ export default async (canvas, config) => {
};
const effectName = config.effect in effects ? config.effect : "palette";
- const pipeline = await makePipeline(context, [makeRain, makeBloomPass, effects[effectName], makeEndPass]);
+ const pipeline = await makePipeline(context, [
+ makeRain,
+ makeBloomPass,
+ effects[effectName],
+ makeEndPass,
+ ]);
const targetFrameTimeMilliseconds = 1000 / config.fps;
let frames = 0;
@@ -107,7 +115,8 @@ export default async (canvas, config) => {
last = start;
}
- const shouldRender = config.fps >= 60 || now - last >= targetFrameTimeMilliseconds || config.once;
+ const shouldRender =
+ config.fps >= 60 || now - last >= targetFrameTimeMilliseconds || config.once;
if (shouldRender) {
while (now - targetFrameTimeMilliseconds > last) {
last += targetFrameTimeMilliseconds;
@@ -125,10 +134,18 @@ export default async (canvas, config) => {
}
if (config.useCamera) {
- device.queue.copyExternalImageToTexture({ source: cameraCanvas }, { texture: cameraTex }, cameraSize);
+ device.queue.copyExternalImageToTexture(
+ { source: cameraCanvas },
+ { texture: cameraTex },
+ cameraSize,
+ );
}
- device.queue.writeBuffer(timeBuffer, 0, timeUniforms.toBuffer({ seconds: (now - start) / 1000, frames }));
+ device.queue.writeBuffer(
+ timeBuffer,
+ 0,
+ timeUniforms.toBuffer({ seconds: (now - start) / 1000, frames }),
+ );
frames++;
const encoder = device.createCommandEncoder();
diff --git a/js/webgpu/mirrorPass.js b/js/webgpu/mirrorPass.js
index 9de12af..e5ec465 100644
--- a/js/webgpu/mirrorPass.js
+++ b/js/webgpu/mirrorPass.js
@@ -1,5 +1,11 @@
import { structs } from "../../lib/gpu-buffer.js";
-import { makeComputeTarget, makeUniformBuffer, loadShader, makeBindGroup, makePass } from "./utils.js";
+import {
+ makeComputeTarget,
+ makeUniformBuffer,
+ loadShader,
+ makeBindGroup,
+ makePass,
+} from "./utils.js";
let start;
const numTouches = 5;
@@ -77,7 +83,11 @@ export default ({ config, device, cameraTex, cameraAspectRatio, timeBuffer }) =>
]);
const screenAspectRatio = size[0] / size[1];
- device.queue.writeBuffer(sceneBuffer, 0, sceneUniforms.toBuffer({ screenAspectRatio, cameraAspectRatio }));
+ device.queue.writeBuffer(
+ sceneBuffer,
+ 0,
+ sceneUniforms.toBuffer({ screenAspectRatio, cameraAspectRatio }),
+ );
return { primary: output };
};
diff --git a/js/webgpu/palettePass.js b/js/webgpu/palettePass.js
index 7e2b479..0f19caa 100644
--- a/js/webgpu/palettePass.js
+++ b/js/webgpu/palettePass.js
@@ -1,6 +1,12 @@
import colorToRGB from "../colorToRGB.js";
import { structs } from "../../lib/gpu-buffer.js";
-import { loadShader, makeUniformBuffer, makeBindGroup, makeComputeTarget, makePass } from "./utils.js";
+import {
+ loadShader,
+ makeUniformBuffer,
+ makeBindGroup,
+ makeComputeTarget,
+ makePass,
+} from "./utils.js";
// Maps the brightness of the rendered rain and bloom to colors
// in a linear gradient buffer generated from the passed-in color sequence
diff --git a/js/webgpu/rainPass.js b/js/webgpu/rainPass.js
index da0f9e3..67bed55 100644
--- a/js/webgpu/rainPass.js
+++ b/js/webgpu/rainPass.js
@@ -1,5 +1,12 @@
import { structs } from "../../lib/gpu-buffer.js";
-import { makeRenderTarget, loadTexture, loadShader, makeUniformBuffer, makeBindGroup, makePass } from "./utils.js";
+import {
+ makeRenderTarget,
+ loadTexture,
+ loadShader,
+ makeUniformBuffer,
+ makeBindGroup,
+ makePass,
+} from "./utils.js";
const rippleTypes = {
box: 0,
@@ -46,7 +53,10 @@ export default ({ config, device, timeBuffer }) => {
// rather than a single quad for our geometry
const numQuads = config.volumetric ? numCells : 1;
- const glyphTransform = mat2.fromScaling(mat2.create(), vec2.fromValues(config.glyphFlip ? -1 : 1, 1));
+ const glyphTransform = mat2.fromScaling(
+ mat2.create(),
+ vec2.fromValues(config.glyphFlip ? -1 : 1, 1),
+ );
mat2.rotate(glyphTransform, glyphTransform, (config.glyphRotation * Math.PI) / 180);
const transform = mat4.create();
@@ -98,10 +108,18 @@ export default ({ config, device, timeBuffer }) => {
let highPassOutput;
const loaded = (async () => {
- const [glyphMSDFTexture, glintMSDFTexture, baseTexture, glintTexture, rainShader] = await Promise.all(assets);
+ const [glyphMSDFTexture, glintMSDFTexture, baseTexture, glintTexture, rainShader] =
+ await Promise.all(assets);
const rainShaderUniforms = structs.from(rainShader.code);
- configBuffer = makeConfigBuffer(device, rainShaderUniforms.Config, config, density, gridSize, glyphTransform);
+ configBuffer = makeConfigBuffer(
+ device,
+ rainShaderUniforms.Config,
+ config,
+ density,
+ gridSize,
+ glyphTransform,
+ );
const introCellsBuffer = device.createBuffer({
size: gridSize[0] * rainShaderUniforms.IntroCell.minSize,
@@ -168,8 +186,17 @@ export default ({ config, device, timeBuffer }) => {
}),
]);
- introBindGroup = makeBindGroup(device, introPipeline, 0, [configBuffer, timeBuffer, introCellsBuffer]);
- computeBindGroup = makeBindGroup(device, computePipeline, 0, [configBuffer, timeBuffer, cellsBuffer, introCellsBuffer]);
+ introBindGroup = makeBindGroup(device, introPipeline, 0, [
+ configBuffer,
+ timeBuffer,
+ introCellsBuffer,
+ ]);
+ computeBindGroup = makeBindGroup(device, computePipeline, 0, [
+ configBuffer,
+ timeBuffer,
+ cellsBuffer,
+ introCellsBuffer,
+ ]);
renderBindGroup = makeBindGroup(device, renderPipeline, 0, [
configBuffer,
timeBuffer,
@@ -196,7 +223,11 @@ export default ({ config, device, timeBuffer }) => {
mat4.perspectiveZO(camera, (Math.PI / 180) * 90, aspectRatio, 0.0001, 1000);
}
const screenSize = aspectRatio > 1 ? [1, aspectRatio] : [1 / aspectRatio, 1];
- device.queue.writeBuffer(sceneBuffer, 0, sceneUniforms.toBuffer({ screenSize, camera, transform }));
+ device.queue.writeBuffer(
+ sceneBuffer,
+ 0,
+ sceneUniforms.toBuffer({ screenSize, camera, transform }),
+ );
// Update
output?.destroy();
diff --git a/js/webgpu/stripePass.js b/js/webgpu/stripePass.js
index 3f709ce..c168c51 100644
--- a/js/webgpu/stripePass.js
+++ b/js/webgpu/stripePass.js
@@ -1,6 +1,13 @@
import colorToRGB from "../colorToRGB.js";
import { structs } from "../../lib/gpu-buffer.js";
-import { loadShader, make1DTexture, makeUniformBuffer, makeBindGroup, makeComputeTarget, makePass } from "./utils.js";
+import {
+ loadShader,
+ make1DTexture,
+ makeUniformBuffer,
+ makeBindGroup,
+ makeComputeTarget,
+ makePass,
+} from "./utils.js";
// Multiplies the rendered rain and bloom by a 1D gradient texture
// generated from the passed-in color sequence
@@ -38,10 +45,15 @@ const numVerticesPerQuad = 2 * 3;
export default ({ config, device, timeBuffer }) => {
// Expand and convert stripe colors into 1D texture data
- const stripeColors = "stripeColors" in config ? config.stripeColors : config.effect === "pride" ? prideStripeColors : transPrideStripeColors;
+ const stripeColors =
+ "stripeColors" in config
+ ? config.stripeColors
+ : config.effect === "pride"
+ ? prideStripeColors
+ : transPrideStripeColors;
const stripeTex = make1DTexture(
device,
- stripeColors.map((color) => [...colorToRGB(color), 1])
+ stripeColors.map((color) => [...colorToRGB(color), 1]),
);
const linearSampler = device.createSampler({
diff --git a/js/webgpu/utils.js b/js/webgpu/utils.js
index f0b63d7..bca4885 100644
--- a/js/webgpu/utils.js
+++ b/js/webgpu/utils.js
@@ -3,7 +3,10 @@ const loadTexture = async (device, url) => {
return device.createTexture({
size: [1, 1, 1],
format: "rgba8unorm",
- usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
+ usage:
+ GPUTextureUsage.TEXTURE_BINDING |
+ GPUTextureUsage.COPY_DST |
+ GPUTextureUsage.RENDER_ATTACHMENT,
});
}
@@ -15,7 +18,10 @@ const loadTexture = async (device, url) => {
const texture = device.createTexture({
size,
format: "rgba8unorm",
- usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
+ usage:
+ GPUTextureUsage.TEXTURE_BINDING |
+ GPUTextureUsage.COPY_DST |
+ GPUTextureUsage.RENDER_ATTACHMENT,
});
device.queue.copyExternalImageToTexture({ source, flipY: true }, { texture }, size);
@@ -28,7 +34,11 @@ const makeRenderTarget = (device, size, format, mipLevelCount = 1) =>
size: [...size, 1],
mipLevelCount,
format,
- usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
+ usage:
+ GPUTextureUsage.TEXTURE_BINDING |
+ GPUTextureUsage.COPY_SRC |
+ GPUTextureUsage.COPY_DST |
+ GPUTextureUsage.RENDER_ATTACHMENT,
});
const makeComputeTarget = (device, size, mipLevelCount = 1) =>
@@ -36,7 +46,11 @@ const makeComputeTarget = (device, size, mipLevelCount = 1) =>
size: [...size, 1],
mipLevelCount,
format: "rgba8unorm",
- usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC | GPUTextureUsage.COPY_DST | GPUTextureUsage.STORAGE_BINDING,
+ usage:
+ GPUTextureUsage.TEXTURE_BINDING |
+ GPUTextureUsage.COPY_SRC |
+ GPUTextureUsage.COPY_DST |
+ GPUTextureUsage.STORAGE_BINDING,
});
const loadShader = async (device, url) => {
@@ -105,4 +119,14 @@ const makePipeline = async (context, steps) => {
};
};
-export { makeRenderTarget, makeComputeTarget, make1DTexture, loadTexture, loadShader, makeUniformBuffer, makePass, makePipeline, makeBindGroup };
+export {
+ makeRenderTarget,
+ makeComputeTarget,
+ make1DTexture,
+ loadTexture,
+ loadShader,
+ makeUniformBuffer,
+ makePass,
+ makePipeline,
+ makeBindGroup,
+};
diff --git a/rollup.config.mjs b/rollup.config.mjs
index f9b8e96..8293306 100644
--- a/rollup.config.mjs
+++ b/rollup.config.mjs
@@ -8,37 +8,37 @@ import terser from "@rollup/plugin-terser";
import { string } from "rollup-plugin-string";
export default {
- input: "js/Matrix.js",
- external: ["react", "react-dom"], // keep them out of your bundle
- plugins: [
- peerDepsExternal(), // auto-exclude peerDeps
- nodeResolve(), // so Rollup can find deps in node_modules
- string({ include: ["**/*.glsl"] }),
- url({ include: ["**/*.png"], limit: 0 }),
- babel({
- exclude: "node_modules/**", // transpile JSX
- babelHelpers: "bundled",
- presets: ["@babel/preset-react", "@babel/preset-env"],
- }),
- commonjs(), // turn CJS deps into ES
- terser({
- sourceMap: false, // <- suppress .map generation
- format: { comments: false },
- }),
- visualizer({
- filename: "dist/stats.html",
- gzipSize: true,
- brotliSize: true,
- includeAssets: true,
- }), // bundle-size treemap
- ],
- output: [
- {
- file: "dist/index.cjs.js",
- format: "cjs",
- exports: "named",
- sourcemap: false,
- },
- // { file: 'dist/index.esm.js', format: 'es' } // optional ESM build
- ],
+ input: "js/Matrix.js",
+ external: ["react", "react-dom"], // keep them out of your bundle
+ plugins: [
+ peerDepsExternal(), // auto-exclude peerDeps
+ nodeResolve(), // so Rollup can find deps in node_modules
+ string({ include: ["**/*.glsl"] }),
+ url({ include: ["**/*.png"], limit: 0 }),
+ babel({
+ exclude: "node_modules/**", // transpile JSX
+ babelHelpers: "bundled",
+ presets: ["@babel/preset-react", "@babel/preset-env"],
+ }),
+ commonjs(), // turn CJS deps into ES
+ terser({
+ sourceMap: false, // <- suppress .map generation
+ format: { comments: false },
+ }),
+ visualizer({
+ filename: "dist/stats.html",
+ gzipSize: true,
+ brotliSize: true,
+ includeAssets: true,
+ }), // bundle-size treemap
+ ],
+ output: [
+ {
+ file: "dist/index.cjs.js",
+ format: "cjs",
+ exports: "named",
+ sourcemap: false,
+ },
+ // { file: 'dist/index.esm.js', format: 'es' } // optional ESM build
+ ],
};
diff --git a/webpack.config.js b/webpack.config.js
index 29a593c..ebafb05 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -3,52 +3,52 @@ const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
- mode: "development",
- entry: path.resolve(__dirname, "./js/index.js"),
- module: {
- rules: [
- {
- test: /\.(js|jsx)$/,
- exclude: /node_modules/,
- use: ["babel-loader"],
- },
- {
- test: /\.css$/,
- use: ["style-loader", "css-loader"],
- },
- {
- test: /\.(png|j?g|svg|gif)?$/,
- type: "asset/resource",
- },
- {
- test: /\.(glsl|frag|vert)$/i,
- exclude: /node_modules/,
- use: ["raw-loader"],
- },
- ],
- },
- resolve: {
- extensions: ["*", ".js", ".jsx"],
- },
- output: {
- path: path.resolve(__dirname, "./dist"),
- filename: "[name].bundle.js",
- clean: true,
- },
- devtool: "inline-source-map",
- plugins: [
- new HtmlWebpackPlugin({
- template: path.resolve(__dirname, "public/index.html"),
- filename: "index.html",
- }),
- new webpack.HotModuleReplacementPlugin(),
- ],
- devServer: {
- historyApiFallback: true,
- static: path.resolve(__dirname, "./dist"),
- compress: true,
- hot: true,
- open: true,
- port: 3000,
- },
+ mode: "development",
+ entry: path.resolve(__dirname, "./js/index.js"),
+ module: {
+ rules: [
+ {
+ test: /\.(js|jsx)$/,
+ exclude: /node_modules/,
+ use: ["babel-loader"],
+ },
+ {
+ test: /\.css$/,
+ use: ["style-loader", "css-loader"],
+ },
+ {
+ test: /\.(png|j?g|svg|gif)?$/,
+ type: "asset/resource",
+ },
+ {
+ test: /\.(glsl|frag|vert)$/i,
+ exclude: /node_modules/,
+ use: ["raw-loader"],
+ },
+ ],
+ },
+ resolve: {
+ extensions: ["*", ".js", ".jsx"],
+ },
+ output: {
+ path: path.resolve(__dirname, "./dist"),
+ filename: "[name].bundle.js",
+ clean: true,
+ },
+ devtool: "inline-source-map",
+ plugins: [
+ new HtmlWebpackPlugin({
+ template: path.resolve(__dirname, "public/index.html"),
+ filename: "index.html",
+ }),
+ new webpack.HotModuleReplacementPlugin(),
+ ],
+ devServer: {
+ historyApiFallback: true,
+ static: path.resolve(__dirname, "./dist"),
+ compress: true,
+ hot: true,
+ open: true,
+ port: 3000,
+ },
};