@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 +67 -0
- package/README.npm.md +30 -0
- package/dist/DigitalGlitchRGB.d.ts +18 -0
- package/dist/DigitalGlitchRGB.d.ts.map +1 -0
- package/dist/DigitalGlitchRGB.js +116 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/package.json +46 -0
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
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|