Glitch art techniques
Always work on copies. Keep the original files and record the settings used for each experiment.
Raw video reinterpretation
Prepare raw data
Convert the video to raw yuv420p. For a 640 × 480 source at 30 fps, include those settings in the output filename:
ffmpeg -i "input.mp4" -c:v rawvideo -pix_fmt yuv420p "output-640x480-yuv420p-30fps.yuv"
Use your actual output dimensions and frame rate. The filename records the settings; it does not set them. This command does not resize or explicitly change the frame rate.
Raw video has no header storing these settings, so you must supply them when opening the file.
Open the file correctly first
Use the settings recorded in the filename:
ffplay -f rawvideo -video_size 640x480 -framerate 30 -pixel_format yuv420p -i "output-640x480-yuv420p-30fps.yuv"
Deliberately reinterpret the bytes
Start with a one-pixel change
Reduce the width by one pixel, from 640 to 639:
ffplay -f rawvideo -video_size 639x480 -framerate 30 -pixel_format yuv420p -i "output-640x480-yuv420p-30fps.yuv"
Or reduce the height by one pixel, from 480 to 479:
ffplay -f rawvideo -video_size 640x479 -framerate 30 -pixel_format yuv420p -i "output-640x480-yuv420p-30fps.yuv"
This small change shifts how the bytes form rows and frames, producing a progressively shifting image. Keep the filename unchanged—it records the original settings.
Try different dimensions, pixel formats or frame rates. List available pixel formats with:
ffmpeg -pix_fmts
Swapping the dimensions
Reinterpret the same file as 480 × 640:
ffplay -f rawvideo -video_size 480x640 -framerate 30 -pixel_format yuv420p -i "output-640x480-yuv420p-30fps.yuv"
Keep the input filename unchanged: it records how the file was created. The changed playback options control how its bytes are reinterpreted.
In this example, swapping width and height keeps the same bytes per frame in yuv420p while changing the row layout. Other changes can shift frame boundaries or leave an incomplete final frame. Changing only the frame rate changes playback speed.
Bake the result into a regular video
Use the same altered input settings that produced the desired glitch:
ffmpeg -f rawvideo -pixel_format yuv420p -video_size 480x640 -framerate 30 -i "output-640x480-yuv420p-30fps.yuv" -c:v libx264 -crf 18 -pix_fmt yuv420p "raw-glitch.mp4"
Sonification and audio databending
This technique interprets video bytes as audio samples, applies audio effects, then interprets the result as video again.
Prepare and import
- Convert the video to a headerless
.yuvfile. - In Audacity, locate the Raw Data import command. The original sheet uses File → Import → Raw Data; the location varies by version.
- Choose A-law or U-law, Mono, start offset 0, and import 100%.
- Record the encoding and sample rate, for example 44100 Hz.
- Fit the project to the window using the zoom controls.
Byte order is irrelevant for these one-byte sample encodings. The audio sample rate determines how quickly the bytes play as sound, independently of the video frame rate.
Use low monitoring volume when auditioning databent material.
Apply effects
Select part or all of the track. Try gain, inversion, EQ or distortion while preserving the exact number of samples.
To preserve video frame alignment, avoid operations that:
- Change duration or sample rate.
- Add an effect tail or silence.
- Insert or delete samples.
Changes to tempo, speed or duration belong to this list of operations to avoid.
Export the modified data
Export audio rather than saving an Audacity project:
- Choose Other uncompressed files, where available.
- Set the header to RAW headerless.
- Use the same encoding, mono channel count and sample rate as the import.
- Export the full original span without extra silence or trimming.
- Disable dithering where the selected format offers it.
Compare the exported byte count with the original.
A-law and U-law are companded encodings, so an untouched import/export cycle is not guaranteed to preserve every byte. A more predictable alternative is unsigned 8-bit PCM for both import and export; check an untouched round trip first.
Preview and bake
Assuming the exported file is glitched.raw and the original settings were 640 × 480, yuv420p, 30 fps:
ffplay -f rawvideo -video_size 640x480 -framerate 30 -pixel_format yuv420p -i "glitched.raw"
Encode the result:
ffmpeg -f rawvideo -pixel_format yuv420p -video_size 640x480 -framerate 30 -i "glitched.raw" -c:v libx264 -crf 18 -pix_fmt yuv420p "audio-databent.mp4"
Validation: the Audacity import/export procedure was reviewed against documentation; the GUI round trip was not tested.
Datamoshing by duplicating P frames
I-frames store independently coded pictures. P-frames use earlier reference pictures. Repeating predictive frames can repeatedly apply motion information, producing smearing.
Prepare an AVI
ffmpeg -i "input.mp4" -vf "scale=640:480,setsar=1,fps=24" -c:v mpeg4 -q:v 2 -g 999 -bf 0 -pix_fmt yuv420p -an "mosh-source.avi"
Here, mpeg4 means MPEG-4 Part 2. The -bf 0 option disables B-frames.
A long GOP reduces frequent resets but does not guarantee that only the first frame is an I-frame.
Inspect the result:
ffprobe -v error -select_streams v:0 -show_entries frame=pict_type -of csv=p=0 "mosh-source.avi"
Duplicate frames
The original workflow uses Avidemux:
- Open the prepared AVI.
- Select one or more P-frames.
- Paste the selection repeatedly at the desired position.
- Keep the initial I-frame.
- Save with Video Output: Copy, Audio Output: Copy if audio exists, and AVI as the output container.
Compatibility: this is an experimental, version-dependent workflow. Avidemux’s documented copy/paste procedure expects I-frame boundaries. A current build may refuse or repair arbitrary P-frame duplication. These GUI steps were not tested.
Preview and bake
After saving the altered file as moshed.avi:
ffplay "moshed.avi"
Encode the decoded result into a fresh video:
ffmpeg -i "moshed.avi" -map 0:v:0 -an -c:v libx264 -crf 18 -pix_fmt yuv420p "moshed-baked.mp4"
Baking records what FFmpeg can decode. It cannot recover undecodable data or guarantee the same appearance as another player.
If a particular player produces the desired look, capturing its playback may preserve that result better.
Datamoshing by joining clips
- Prepare both clips using the same MPEG-4 Part 2 encoder, dimensions, pixel format and frame rate.
- Open the first clip in an editor that supports encoded-frame manipulation.
- Append the second clip.
- Preserve the first clip’s initial I-frame.
- Remove the second clip’s initial I-frame while retaining its following P-frames.
- Later I-frames can reset the effect. Inspect and remove additional resets if desired.
- Save in copy mode to AVI, preview, then bake.
This deliberately breaks prediction references. Equal codec names and dimensions alone do not guarantee compatible stream parameters.
Avidemux may reject or change the operation, depending on version. Re-encoding during the editing step can erase the intended manipulation.
Scripted datamoshing with Tomato
The original sheet appears to describe a different Tomato revision from the upstream itsKaspar/tomato v2 source reviewed during validation. Record the exact script revision used for a workshop.
Prepare a silent AVI using the MPEG-4 command above.
Inspect the script’s help:
python3 tomato.py -h
The reviewed upstream source supports void, random, reverse, invert, bloom, pulse, jiggle and overlap.
It has no ikill, iswap, shuffle or overlapped mode. Unknown modes may produce unusable output.
Default frame removal
python3 tomato.py -i "mosh-source.avi" -m void
This retains the initial video chunk and filters chunks using a size threshold. It does not reliably identify every I-frame.
Bloom
Insert 50 copies of the item at zero-based index 100 in the filtered working list:
python3 tomato.py -i "mosh-source.avi" -m bloom -c 50 -n 100
The working list must be long enough. Its index 100 is not necessarily source frame 100.
Random order
Randomize the filtered list:
python3 tomato.py -i "mosh-source.avi" -m random
Overlapping groups
Copy groups of four list items at a stride of two:
python3 tomato.py -i "mosh-source.avi" -m overlap -c 4 -n 2
Known limitations
The reviewed pulse implementation ignores -n and has a counter error. The original “every tenth frame five times” description is therefore unsupported. Avoid this mode until repaired.
jiggle also carries an upstream broken-status warning.
Tomato uses heuristic AVI parsing and may leave stale headers or indexes. It can overwrite an existing output with the same generated filename. Use a separate experiment folder, preview each result, then bake the chosen AVI.
Validation: these examples were checked against the upstream source, not executed.
Batch editing an image sequence
Extract frames
Create a dedicated folder and export numbered BMP files:
mkdir -p frames-original
ffmpeg -i "input.mp4" -fps_mode passthrough "frames-original/%04d.bmp"
Modify pixel data
The original sed loop has several problems:
- It processes every item in the current folder.
- Its unquoted filename variable mishandles spaces.
- It can modify file headers.
- In GNU sed,
\xEDAmeans byteEDfollowed by ASCIIA, expanding one byte into two.
The following Python script changes byte DA to byte ED without changing file lengths. It processes uncompressed 24-bit BMPs, preserves headers, and writes new files:
from pathlib import Path
import struct
source = Path("frames-original")
target = Path("frames-glitched")
target.mkdir(exist_ok=True)
for path in sorted(source.glob("*.bmp")):
data = bytearray(path.read_bytes())
if len(data) < 54 or data[:2] != b"BM":
print(f"Skipped invalid BMP: {path.name}")
continue
offset = struct.unpack_from("<I", data, 10)[0]
dib_size = struct.unpack_from("<I", data, 14)[0]
width, height = struct.unpack_from("<ii", data, 18)
planes, depth = struct.unpack_from("<HH", data, 26)
compression = struct.unpack_from("<I", data, 30)[0]
if (dib_size < 40 or offset < 14 + dib_size or width <= 0
or height == 0 or planes != 1 or depth != 24
or compression != 0):
print(f"Skipped unsupported BMP: {path.name}")
continue
stride = ((width * 3 + 3) // 4) * 4
end = offset + stride * abs(height)
if end > len(data):
print(f"Skipped truncated BMP: {path.name}")
continue
data[offset:end] = data[offset:end].replace(b"\xDA", b"\xED")
with (target / path.name).open("xb") as output:
output.write(data)
Save as glitch_bmps.py and run:
python3 glitch_bmps.py
Change the two single-byte values to experiment. The effect may be subtle if the chosen byte rarely occurs.
Unsupported BMP layouts are skipped. Existing output files cause an error instead of being overwritten.
Preview
ffplay -framerate 30 -start_number 1 -i "frames-glitched/%04d.bmp"
Bake
ffmpeg -framerate 30 -start_number 1 -i "frames-glitched/%04d.bmp" -c:v libx264 -crf 18 -pix_fmt yuv420p "bmp-glitch.mp4"
Use the intended source frame rate. Preserving variable-frame-rate timing requires separate timestamp handling.
Validation: the script was tested on 150 BMP frames. It preserved all headers and file lengths, and the resulting video decoded successfully.
Hexadecimal and text editing
Prepare
Make a copy of the source. Try different encodings or a raw file.
Raw video is useful for initial experiments because it avoids container headers, but you must separately record its dimensions, pixel format and frame rate.
Edit
Open the copy in HxD or another binary editor.
Try replacing byte values or sequences, or copying a region from another file. Start with small regions and equal-length replacements.
Preserve container headers and indexes when working with structured files. Keeping only the initial header intact is insufficient: packet lengths, checksums, codec headers and prediction data may also matter.
Ordinary text editors can change encoding, line endings or file length unexpectedly.
Bake what can be decoded
For a readable corrupted AVI:
ffmpeg -i "hex-edited.avi" -map 0:v:0 -an -c:v libx264 -crf 18 -pix_fmt yuv420p "hex-edited-baked.mp4"
For raw data, use the explicit raw input settings shown earlier.
Re-encoding preserves what the selected decoder can render; it does not repair every corrupted file.
No comments to display
No comments to display