imageclown
Library to create image-sequence based videos in Node.js using a combination of Imagemagick and FFMPEG.
Purpose
Linear video-editing software like Premiere is not super well-suited for making videos with precise frame-by-frame arrangements. imageclown attempts to demonstrate a new way of making videos with code.
In a """nutshell""", you can import source images and videos as frames
, create chunks
of individual frames with provided scalpel
and patterns
methods, and render chunks
into videos.
This library was used to make the music video for Perc's track "Unelected".
Examples
Further documentation should come soon, but below are some example scripts. For now, you should just clone the repository
and add your code directly to the designated block in main.js
. I still need to design the public api that will allow
you to simply run imageclown ${script_name}
from the command line.
Overlay
let politics2Layers = new Chunk('politics2').repeat(8).map((f, idx) => {
let p = `${75 - idx * 8}%`
return f.cropResize({ width: p, height: p })
})
let politics1 = new Chunk('politics1').overlay(politics2Layers).crop().repeat(64)
let chunk = politics1.beatSpace('black', { interval: fpb / 2, imageLength: 2, chunkLength: 3 })
return chunk
Waterfalling
let black = mc(gen.color('black'))
let p5 = cc('image1')
let chunk1 = scalpel.waterfall(black, p5).repeat(fpb)
let colors = mc([gen.color('red')), gen.color('blue')])
let uk = cc(['ukpolitics1', 'ukpolitics2'])
let chunk2 = scalpel.waterfall(colors, uk, { vertical: false }).repeat(fpb)
let chunk = chunk1.add(chunk2).repeat(2)
return chunk
Globbing and Blending
let chunk1 = cc(glob('image*'))
let chunk = mc([
scalpel.blendAverage(chunk1, 'divide'),
scalpel.blend(chunk1, 'colorburn')
])
.multiply(2)
.repeat(fpb * 4)
return chunk
Grid
let chunk1 = cc([
'image1',
'image2',
'image3',
])
let chunk = mc([
scalpel.grid(chunk1),
scalpel.grid(chunk1.shift(1)),
scalpel.grid(chunk1.shift(2)),
gen.color('black'),
gen.color('black')
])
.multiply(3)
.repeat(8)
return chunk
Video Chunk
let gridvid = vc('video1')
let colors = cc([gen.color('black'), gen.color('red'), gen.color('blue')])
colors = scalpel.splice(colors, { divisions: 8 })
let chunk = gridvid.beatSpace(colors, { interval: fpb, chunkLength: 4 }).repeat(4)
return chunk
Blend Waterfall
let black = mc(gen.color('black'))
let chunk1 = scalpel.waterfall(black, cc('image1'), { cuts: 2 }).multiply(4).repeat(fpb)
let chunk2 = scalpel.waterfall(black, cc('image2'), { cuts: 3, vertical: false }).multiply(2).repeat(fpb)
let chunk3 = scalpel.waterfall(black, cc('image3'), { cuts: 4 }).multiply(7).repeat(fpb)
let chunk4 = scalpel.waterfall(black, cc('image4'), { cuts: 5, vertical: false }).multiply(3)
let chunk = chunk1.merge([chunk2, chunk3, chunk4], frames => scalpel.blend(frames, 'modulusadd'))
return chunk
Tiling
let black = mc(gen.color('black'))
let politics = cc(glob('images*'))
let tiles = mc([
scalpel.tile(politics),
scalpel.tile(politics.shift(1)),
scalpel.tile(politics.shift(2)),
scalpel.tile(politics.shift(3))
])
let chunk = black.intersperse(tiles).repeat(fpb * 4)
return chunk
Masking with Patterns
let p5 = cc('image1')
let w9 = cc('image2')
let vmask = mask.pattern({ pattern: 'vertical2' })
let hmask = mask.pattern({ pattern: 'horizontal2' })
let hexmask = mask.pattern({ pattern: 'hexagons', tile: true, maskWidth: 200 })
let sawmask = mask.pattern({ pattern: 'horizontalsaw', maskWidth: 100, tile: true })
let chunk = new Chunk([
p5.maskInvert(w9, vmask),
p5.maskInvert(w9, hexmask),
p5.maskInvert(w9, hmask),
p5.maskInvert(w9, sawmask)
])
.repeat(fpb * 2)
return chunk
Diagonal Mask / Multimasking
let politics = cc(['image1', 'image2', 'image3'])
let drone = cc('image4')
let masks = cc([mask.diagonal(45), mask.diagonal(-45)])
let chunk = scalpel.multimask(politics, drone, masks).repeat(fpb * 4)
return chunk
Random / Plasma Masks
let politics = cc(['image1', 'image2', 'image3'])
let drone = cc('image4')
let masks1 = [5, 10, 15, 20].map(i => mask.random({ blur: i, solarize: '50%' }))
let masks2 = patterns.range(1, 4).map(i => mask.random({ blur: 16, fn: `Sinusoid ${i},90` }))
let masks = patterns.intersperse([masks1, masks2])
let chunk = scalpel.multimask(politics, drone, masks).repeat(fpb * 2)
return chunk
Random Cuts
let trump = cc('image1')
let drone = cc('image2')
let chunk = scalpel.randomCuts(drone, trump)
.add(scalpel.randomCuts(drone, trump, { build: true, cutsPerFrame: 2 }))
.multiply(2)
.repeat(4)
return chunk