@storybynumbers_/remotion-glitch-effect 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # @storybynumbers_/remotion-glitch-effect
2
+
3
+ Digital glitch effect with RGB channel splitting for Remotion.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @storybynumbers_/remotion-glitch-effect
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```tsx
14
+ import { DigitalGlitchRGB } from '@storybynumbers_/remotion-glitch-effect';
15
+
16
+ <DigitalGlitchRGB
17
+ splitAmount={5}
18
+ blurAmount={0.8}
19
+ burstSpacing={28}
20
+ >
21
+ <YourContent />
22
+ </DigitalGlitchRGB>
23
+ ```
24
+
25
+ ## Props
26
+
27
+ | Prop | Type | Default | Description |
28
+ |------|------|---------|-------------|
29
+ | `splitAmount` | `number` | `4` | RGB offset in pixels (2-8 recommended) |
30
+ | `blurAmount` | `number` | `0.8` | Blur radius (0-2 recommended) |
31
+ | `jitterAmount` | `number` | `1.5` | Global shake in pixels (0-3 recommended) |
32
+ | `burstSpacing` | `number` | `25` | Average frames between bursts (15-45) |
33
+ | `burstDuration` | `[number, number]` | `[2, 5]` | Min/max burst length in frames |
34
+ | `seed` | `number` | `42` | Deterministic randomization seed |
35
+
36
+ ## Examples
37
+
38
+ **Subtle phone interference**
39
+ ```tsx
40
+ <DigitalGlitchRGB splitAmount={3} blurAmount={0.5} burstSpacing={35}>
41
+ {children}
42
+ </DigitalGlitchRGB>
43
+ ```
44
+
45
+ **Aggressive glitch**
46
+ ```tsx
47
+ <DigitalGlitchRGB splitAmount={7} jitterAmount={2.5} burstSpacing={18}>
48
+ {children}
49
+ </DigitalGlitchRGB>
50
+ ```
51
+
52
+ ## How It Works
53
+
54
+ - Splits RGB channels using SVG filters (`feOffset`)
55
+ - Pre-computes burst schedule for sparse, timed glitches
56
+ - Per-frame variation within bursts for organic feel
57
+ - Zero overhead when stable (no filters applied)
58
+ - Fully deterministic (reproducible renders)
59
+
60
+ ## Requirements
61
+
62
+ - `remotion`: ^4.0.0
63
+ - `react`: ^18.0.0
64
+
65
+ ## License
66
+
67
+ MIT
package/README.npm.md ADDED
@@ -0,0 +1,30 @@
1
+ # @storybynumbers_/remotion-glitch-effect
2
+
3
+ Digital glitch effect with RGB channel splitting for Remotion.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @storybynumbers_/remotion-glitch-effect
9
+ ```
10
+
11
+ ## Quick use
12
+
13
+ ```tsx
14
+ import { DigitalGlitchRGB } from '@storybynumbers_/remotion-glitch-effect';
15
+
16
+ <DigitalGlitchRGB splitAmount={5} blurAmount={0.8} burstSpacing={28}>
17
+ <YourContent />
18
+ </DigitalGlitchRGB>
19
+ ```
20
+
21
+ ## Props (core)
22
+
23
+ - `splitAmount` (number, default 4) RGB offset in pixels
24
+ - `blurAmount` (number, default 0.8) Blur radius
25
+ - `jitterAmount` (number, default 1.5) Global shake in pixels
26
+ - `burstSpacing` (number, default 25) Avg frames between bursts
27
+ - `burstDuration` ([number, number], default [2, 5]) Min/max burst length
28
+ - `seed` (number, default 42) Deterministic randomization seed
29
+
30
+ Full docs and examples: https://github.com/storybynumbers/remotion-glitch-effect#readme
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ export interface DigitalGlitchRGBProps {
3
+ children: React.ReactNode;
4
+ /** Max RGB offset in pixels during bursts. Range: 2-8. Default: 4 */
5
+ splitAmount?: number;
6
+ /** Blur radius during bursts. Range: 0-2. Default: 0.8 */
7
+ blurAmount?: number;
8
+ /** Global jitter in pixels. Range: 0-3. Default: 1.5 */
9
+ jitterAmount?: number;
10
+ /** Average frames between bursts. Range: 15-45. Default: 25 */
11
+ burstSpacing?: number;
12
+ /** Burst duration range [min, max] in frames. Default: [2, 5] */
13
+ burstDuration?: [number, number];
14
+ /** Seed for deterministic randomization. Default: 42 */
15
+ seed?: number;
16
+ }
17
+ export declare const DigitalGlitchRGB: React.FC<DigitalGlitchRGBProps>;
18
+ //# sourceMappingURL=DigitalGlitchRGB.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DigitalGlitchRGB.d.ts","sourceRoot":"","sources":["../lib/DigitalGlitchRGB.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyB,MAAM,OAAO,CAAC;AAU9C,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,qEAAqE;IACrE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+DAA+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAuED,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAuI5D,CAAC"}
@@ -0,0 +1,116 @@
1
+ import React, { useMemo, useId } from 'react';
2
+ import { AbsoluteFill, random, useCurrentFrame } from 'remotion';
3
+ /**
4
+ * Attempt to generate a burst at each spacing interval.
5
+ * Returns array of non-overlapping bursts.
6
+ */
7
+ function generateBursts(totalFrames, spacing, durationRange, seed) {
8
+ const bursts = [];
9
+ let frame = 0;
10
+ let burstIndex = 0;
11
+ while (frame < totalFrames) {
12
+ const burstSeed = seed + burstIndex * 1000;
13
+ // Jitter the start time within the spacing window
14
+ const jitter = Math.floor((random(`burst-jitter-${burstSeed}`) - 0.5) * spacing * 0.6);
15
+ const startFrame = Math.max(0, frame + Math.floor(spacing / 2) + jitter);
16
+ // Random duration within range
17
+ const duration = Math.floor(durationRange[0] +
18
+ random(`burst-dur-${burstSeed}`) * (durationRange[1] - durationRange[0]));
19
+ // Vary peak intensity (0.6 - 1.0)
20
+ const peakIntensity = 0.6 + random(`burst-peak-${burstSeed}`) * 0.4;
21
+ if (startFrame < totalFrames) {
22
+ bursts.push({ startFrame, duration, peakIntensity, seed: burstSeed });
23
+ }
24
+ frame = startFrame + duration + Math.floor(spacing * 0.3); // Gap before next possible burst
25
+ burstIndex++;
26
+ }
27
+ return bursts;
28
+ }
29
+ /**
30
+ * Calculate glitch intensity g(t) for current frame.
31
+ * Returns 0 when stable, 0-1 during bursts.
32
+ * Fast attack, slightly slower decay.
33
+ */
34
+ function getGlitchIntensity(frame, bursts) {
35
+ for (const burst of bursts) {
36
+ const { startFrame, duration, peakIntensity } = burst;
37
+ const endFrame = startFrame + duration;
38
+ if (frame >= startFrame && frame < endFrame) {
39
+ const localFrame = frame - startFrame;
40
+ // Fast attack (1 frame), plateau, slight decay (1 frame)
41
+ let envelope = 1;
42
+ if (localFrame === 0) {
43
+ envelope = 0.7; // Quick ramp in
44
+ }
45
+ else if (localFrame === duration - 1 && duration > 2) {
46
+ envelope = 0.5; // Slight decay out
47
+ }
48
+ return { intensity: peakIntensity * envelope, burst };
49
+ }
50
+ }
51
+ return { intensity: 0, burst: null };
52
+ }
53
+ export const DigitalGlitchRGB = ({ children, splitAmount = 4, blurAmount = 0.8, jitterAmount = 1.5, burstSpacing = 25, burstDuration = [2, 5], seed = 42, }) => {
54
+ const frame = useCurrentFrame();
55
+ const reactId = useId();
56
+ const filterId = useMemo(() => `glitch-rgb-${reactId.replace(/:/g, '')}`, [reactId]);
57
+ // Pre-generate burst schedule (memoized, doesn't change during playback)
58
+ // Assume max 600 frames (~20s at 30fps) for generation
59
+ const bursts = useMemo(() => generateBursts(600, burstSpacing, burstDuration, seed), [burstSpacing, burstDuration, seed]);
60
+ // Get current glitch state
61
+ const { intensity, burst } = getGlitchIntensity(frame, bursts);
62
+ const isGlitching = intensity > 0 && burst !== null;
63
+ // Per-frame noise for variation within bursts
64
+ const frameSeed = burst ? burst.seed + frame : seed + frame;
65
+ // RGB channel offsets (unequal to avoid pure "camera shake" look)
66
+ // Red shifts right/up, Blue shifts left/down, Green stays centered
67
+ const redOffsetX = isGlitching
68
+ ? intensity * splitAmount * (0.8 + random(`r-ox-${frameSeed}`) * 0.4)
69
+ : 0;
70
+ const redOffsetY = isGlitching
71
+ ? intensity * splitAmount * 0.3 * (random(`r-oy-${frameSeed}`) - 0.3)
72
+ : 0;
73
+ const blueOffsetX = isGlitching
74
+ ? -intensity * splitAmount * (0.8 + random(`b-ox-${frameSeed}`) * 0.4)
75
+ : 0;
76
+ const blueOffsetY = isGlitching
77
+ ? intensity * splitAmount * 0.3 * (random(`b-oy-${frameSeed}`) - 0.7)
78
+ : 0;
79
+ // Global micro-jitter (applies to whole image)
80
+ const globalJitterX = isGlitching
81
+ ? intensity * jitterAmount * (random(`jit-x-${frameSeed}`) * 2 - 1)
82
+ : 0;
83
+ const globalJitterY = isGlitching
84
+ ? intensity * jitterAmount * (random(`jit-y-${frameSeed}`) * 2 - 1)
85
+ : 0;
86
+ // Small scale pulse during bursts
87
+ const scalePulse = isGlitching
88
+ ? 1 + intensity * 0.003 * (random(`scale-${frameSeed}`) * 2 - 1)
89
+ : 1;
90
+ // Blur radius
91
+ const blur = isGlitching ? intensity * blurAmount : 0;
92
+ // When not glitching, render children directly (no filter overhead)
93
+ if (!isGlitching) {
94
+ return React.createElement(AbsoluteFill, null, children);
95
+ }
96
+ return (React.createElement(AbsoluteFill, null,
97
+ React.createElement("svg", { style: { position: 'absolute', width: 0, height: 0 } },
98
+ React.createElement("defs", null,
99
+ React.createElement("filter", { id: `${filterId}-red`, x: "-10%", y: "-10%", width: "120%", height: "120%" },
100
+ React.createElement("feColorMatrix", { type: "matrix", values: "1 0 0 0 0\n 0 0 0 0 0\n 0 0 0 0 0\n 0 0 0 1 0" }),
101
+ React.createElement("feOffset", { dx: redOffsetX, dy: redOffsetY }),
102
+ blur > 0 && React.createElement("feGaussianBlur", { stdDeviation: blur * 0.5 })),
103
+ React.createElement("filter", { id: `${filterId}-green` },
104
+ React.createElement("feColorMatrix", { type: "matrix", values: "0 0 0 0 0\n 0 1 0 0 0\n 0 0 0 0 0\n 0 0 0 1 0" }),
105
+ blur > 0 && React.createElement("feGaussianBlur", { stdDeviation: blur * 0.3 })),
106
+ React.createElement("filter", { id: `${filterId}-blue`, x: "-10%", y: "-10%", width: "120%", height: "120%" },
107
+ React.createElement("feColorMatrix", { type: "matrix", values: "0 0 0 0 0\n 0 0 0 0 0\n 0 0 1 0 0\n 0 0 0 1 0" }),
108
+ React.createElement("feOffset", { dx: blueOffsetX, dy: blueOffsetY }),
109
+ blur > 0 && React.createElement("feGaussianBlur", { stdDeviation: blur * 0.5 })))),
110
+ React.createElement(AbsoluteFill, { style: {
111
+ transform: `translate(${globalJitterX}px, ${globalJitterY}px) scale(${scalePulse})`,
112
+ } },
113
+ React.createElement(AbsoluteFill, { style: { filter: `url(#${filterId}-red)`, mixBlendMode: 'screen' } }, children),
114
+ React.createElement(AbsoluteFill, { style: { filter: `url(#${filterId}-green)`, mixBlendMode: 'screen' } }, children),
115
+ React.createElement(AbsoluteFill, { style: { filter: `url(#${filterId}-blue)`, mixBlendMode: 'screen' } }, children))));
116
+ };
@@ -0,0 +1,3 @@
1
+ export { DigitalGlitchRGB } from './DigitalGlitchRGB';
2
+ export type { DigitalGlitchRGBProps } from './DigitalGlitchRGB';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,YAAY,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { DigitalGlitchRGB } from './DigitalGlitchRGB';
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@storybynumbers_/remotion-glitch-effect",
3
+ "version": "1.0.0",
4
+ "description": "Digital glitch effect with RGB channel splitting for Remotion",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist",
9
+ "README.npm.md"
10
+ ],
11
+ "scripts": {
12
+ "start": "remotion preview src/index.tsx",
13
+ "preview": "remotion preview src/index.tsx",
14
+ "render": "remotion render src/index.tsx",
15
+ "build": "remotion build src/index.tsx",
16
+ "build:lib": "tsc -p tsconfig.lib.json",
17
+ "prepublishOnly": "npm run build:lib"
18
+ },
19
+ "keywords": [
20
+ "remotion",
21
+ "glitch",
22
+ "rgb",
23
+ "effect",
24
+ "video",
25
+ "vfx"
26
+ ],
27
+ "author": "Story by Numbers",
28
+ "license": "MIT",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/storybynumbers/remotion-glitch-effect.git"
32
+ },
33
+ "peerDependencies": {
34
+ "react": "^18.0.0",
35
+ "remotion": "^4.0.0"
36
+ },
37
+ "devDependencies": {
38
+ "@remotion/cli": "^4.0.0",
39
+ "@types/react": "^18.2.22",
40
+ "@types/react-dom": "^18.2.7",
41
+ "react": "^18.2.0",
42
+ "react-dom": "^18.2.0",
43
+ "remotion": "^4.0.0",
44
+ "typescript": "^5.3.3"
45
+ }
46
+ }