Home

Awesome

Discord NPM version Build status PayPal

Editly is a tool and framework for declarative NLE (non-linear video editing) using Node.js and ffmpeg. Editly allows you to easily and programmatically create a video from a set of clips, images, audio and titles, with smooth transitions and music overlaid.

Editly has a simple CLI for quickly assembling a video from a set of clips or images, or you can use its more flexible JavaScript API.

Inspired by ffmpeg-concat, editly is much faster and doesn't require much storage because it uses streaming editing. Editly aims to be very extensible and feature rich with a pluggable interface for adding new dynamic content.

demo

This GIF / YouTube was created with this command: "editly commonFeatures.json5". See more examples here.

Features

Use cases

See examples

Requirements

Installing

npm i -g editly

Usage: Command line video editor

Run editly --help for usage

Create a simple randomized video edit from videos, images and text with an audio track:

editly \
  title:'My video' \
  clip1.mov \
  clip2.mov \
  title:'My slideshow' \
  img1.jpg \
  img2.jpg \
  title:'THE END' \
  --fast \
  --audio-file-path /path/to/music.mp3

Or create an MP4 (or GIF) from a JSON or JSON5 edit spec (JSON5 is just a more user friendly JSON format):

editly my-spec.json5 --fast --keep-source-audio --out output.gif

For examples of how to make a JSON edit spec, see below or examples.

Without --fast, it will default to using the width, height and frame rate from the first input video. All other clips will be converted to these dimensions. You can of course override any or all of these parameters.

JavaScript library

import editly from 'editly';

// See editSpec documentation
await editly(editSpec)

Edit spec

Edit specs are JavaScript / JSON objects describing the whole edit operation with the following structure:

{
  outPath,
  width,
  height,
  fps,
  allowRemoteRequests: false,
  defaults: {
    duration: 4,
    transition: {
      duration: 0.5,
      name: 'random',
      audioOutCurve: 'tri',
      audioInCurve: 'tri',
    },
    layer: {
      fontPath,
      // ...more layer defaults
    },
    layerType: {
      'fill-color': {
        color: '#ff6666',
      }
      // ...more per-layer-type defaults
    },
  },
  clips: [
    {
      transition,
      duration,
      layers: [
        {
          type,
          // ...more layer-specific options
        }
        // ...more layers
      ],
    }
    // ...more clips
  ],
  audioFilePath,
  loopAudio: false,
  keepSourceAudio: false,
  clipsAudioVolume: 1,
  outputVolume: 1,
  audioTracks: [
    {
      path,
      mixVolume: 1,
      cutFrom: 0,
      cutTo,
      start: 0,
    },
    // ...more audio tracks
  ],
  audioNorm: {
    enable: false,
    gaussSize: 5,
    maxGain: 30,
  }

  // Testing options:
  enableFfmpegLog: false,
  verbose: false,
  fast: false,
}

Parameters

ParameterCLI equivalentDescriptionDefault
outPath--outOutput path (mp4, mkv), can also be a .gif
width--widthWidth which all media will be converted to640
height--heightHeight which all media will be converted toauto based on width and aspect ratio of first video
fps--fpsFPS which all videos will be converted toFirst video FPS or 25
customOutputArgsSpecify custom output codec/format arguments for ffmpeg (See example)auto (h264)
allowRemoteRequests--allow-remote-requestsAllow remote URLs as pathsfalse
fast--fast, -fFast mode (low resolution and FPS, useful for getting a quick preview ⏩)false
defaults.layer.fontPath--font-pathSet default font to a .ttfSystem font
defaults.layer.*Set any layer parameter that all layers will inherit
defaults.duration--clip-durationSet default clip duration for clips that don't have an own duration4sec
defaults.transitionAn object { name, duration } describing the default transition. Set to null to disable transitions
defaults.transition.duration--transition-durationDefault transition duration0.5sec
defaults.transition.name--transition-nameDefault transition type. See Transition typesrandom
defaults.transition.audioOutCurveDefault fade out curve in audio cross fadestri
defaults.transition.audioInCurveDefault fade in curve in audio cross fadestri
clips[]List of clip objects that will be played in sequence. Each clip can have one or more layers.
clips[].durationClip duration. See defaults.duration. If unset, the clip duration will be that of the first video layer.defaults.duration
clips[].transitionSpecify transition at the end of this clip. See defaults.transitiondefaults.transition
clips[].layers[]List of layers within the current clip that will be overlaid in their natural order (final layer on top)
clips[].layers[].typeLayer type, see below
clips[].layers[].startWhat time into the clip should this layer startsec
clips[].layers[].stopWhat time into the clip should this layer stopsec
audioTracks[]List of arbitrary audio tracks. See audio tracks.[]
audioFilePath--audio-file-pathSet an audio track for the whole video. See also audio tracks
loopAudio--loop-audioLoop the audio track if it is shorter than video?false
keepSourceAudio--keep-source-audioKeep source audio from clips?false
clipsAudioVolumeVolume of audio from clips relative to audioTracks. See audio tracks.1
outputVolume--output-volumeAdjust output volume (final stage). See example1e.g. 0.5 or 10dB
audioNorm.enableEnable audio normalization? See audio normalization.false
audioNorm.gaussSizeAudio normalization gauss size. See audio normalization.5
audioNorm.maxGainAudio normalization max gain. See audio normalization.30

Transition types

transition.name can be any of gl-transitions, or any of the following: directional-left, directional-right, directional-up, directional-down, random or dummy.

Layer types

See examples and commonFeatures.json5

Layer type 'video'

For video layers, if parent clip.duration is specified, the video will be slowed/sped-up to match clip.duration. If cutFrom/cutTo is set, the resulting segment (cutTo-cutFrom) will be slowed/sped-up to fit clip.duration. If the layer has audio, it will be kept (and mixed with other audio layers if present.)

ParameterDescriptionDefault
pathPath to video file
resizeModeSee Resize modes
cutFromTime value to cut from0sec
cutToTime value to cut toend of videosec
widthWidth relative to screen width10 to 1
heightHeight relative to screen height10 to 1
leftX-position relative to screen width00 to 1
topY-position relative to screen height00 to 1
originXX anchorleftleft or right
originYY anchortoptop or bottom
mixVolumeRelative volume when mixing this video's audio track with others1

Layer type 'audio'

Audio layers will be mixed together. If cutFrom/cutTo is set, the resulting segment (cutTo-cutFrom) will be slowed/sped-up to fit clip.duration. The slow down/speed-up operation is limited to values between 0.5x and 100x.

ParameterDescriptionDefault
pathPath to audio file
cutFromTime value to cut from0sec
cutToTime value to cut toclip.durationsec
mixVolumeRelative volume when mixing this audio track with others1

Layer type 'detached-audio'

This is a special case of audioTracks that makes it easier to start the audio relative to clips start times without having to calculate global start times.

detached-audio has the exact same properties as audioTracks, except start time is relative to the clip's start.

Example of detached audio tracks

Layer type 'image'

Full screen image

ParameterDescriptionDefault
pathPath to image file
resizeModeSee Resize modes

See also See Ken Burns parameters.

Layer type 'image-overlay'

Image overlay with a custom position and size on the screen. NOTE: If you want to use animated GIFs use video instead.

ParameterDescriptionDefault
pathPath to image file
positionSee Position parameter
widthWidth (from 0 to 1) where 1 is screen width
heightHeight (from 0 to 1) where 1 is screen height

See also Ken Burns parameters.

Layer type 'title'

See also Ken Burns parameters

Layer type 'subtitle'

Layer type 'title-background'

Title with background

Layer type 'news-title'

Layer type 'slide-in-text'

Layer type 'fill-color', 'pause'

Layer type 'radial-gradient'

Layer type 'linear-gradient'

Layer type 'rainbow-colors'

🌈🌈🌈

Layer type 'canvas'

See customCanvas.js

Layer type 'fabric'

See customFabric.js

Layer type 'gl'

Loads a GLSL shader. See gl.json5 and rainbow-colors.frag

Arbitrary audio tracks

audioTracks property can optionally contain a list of objects which specify audio tracks that can be started at arbitrary times in the final video. These tracks will be mixed together (mixVolume specifying a relative number for how loud each track is compared to the other tracks). Because audio from clips will be mixed separately from audioTracks, clipsAudioVolume specifies the volume of the combined audio from clips relative to the volume of each of the audio tracks from audioTracks.

ParameterDescriptionDefault
audioTracks[].pathFile path for this track
audioTracks[].mixVolumeRelative volume for this track1
audioTracks[].cutFromTime value to cut source file from0sec
audioTracks[].cutToTime value to cut source file tosec
audioTracks[].startHow many seconds into video to start this audio track0sec

The difference between audioTracks and Layer type 'audio' is that audioTracks will continue to play across multiple clips and can start and stop whenever needed.

See audioTracks example

See also Layer type 'detached-audio'.

Audio normalization

You can enable audio normalization of the final output audio. This is useful if you want to achieve Audio Ducking (e.g. automatically lower volume of all other tracks when voice-over speaks).

audioNorm parameters are documented here.

Example of audio ducking

Resize modes

resizeMode - How to fit image to screen. Can be one of:

Default contain-blur.

See:

Position parameter

Certain layers support the position parameter

position can be one of either:

See position.json5

Ken Burns parameters

ParameterDescriptionDefault
zoomDirectionZoom direction for Ken Burns effect: in, out, left, right or null to disable
zoomAmountZoom amount for Ken Burns effect0.1

Docker

This should help you use editly as a containerized CLI, without worrying about getting all the right versions of dependencies on your system.

$ git clone https://github.com/mifi/editly.git
$ cd editly/examples
$ git clone https://github.com/mifi/editly-assets.git assets
$ cd ..
$ docker-compose up
$ docker-compose run editly bash -c "cd examples && editly audio1.json5 --out /outputs/audio1.mp4"
$ docker cp editly:/outputs/audio1.mp4 .

Troubleshooting

Donate 🙏

This project is maintained by me alone. The project will always remain free and open source, but if it's useful for you, consider supporting me. :) It will give me extra motivation to improve it.

Paypal

Thanks

This package would not exist without the support and help from all the contributors and sponsors!

Special thanks to:

See also

Videos made by you

Submit a PR if you want to share your videos or project created with editly here.


Made with ❤️ in 🇳🇴

More apps by mifi.no

Follow me on GitHub, YouTube, IG, Twitter for more awesome content!