To embed these individually append ?file="FILENAME" to the embed src link.
<script src="https://gist.github.com/fartinmartin/f6fe63328817a6eff17a07e60b2a6eb1.js?file=lookAtLayer.jsx"></script>
To embed these individually append ?file="FILENAME" to the embed src link.
<script src="https://gist.github.com/fartinmartin/f6fe63328817a6eff17a07e60b2a6eb1.js?file=lookAtLayer.jsx"></script>
| # from the directory containing the source files | |
| for x in *.webp; do ffmpeg -i "$x" "${x%.*}.jpg"; done | |
| # ^input file extention ^output file extension |
| # text file with links on new line | |
| youtube-dl -a links.txt | |
| # if video's are overwriten/skipped, use the id etc to allow all to download: | |
| youtube-dl -a links.txt -o '%(id)s_%(title)s_%(url)s_%(autonumber)s' |
| // blend between two expressions using a slider | |
| blend = thisComp.layer("Control").effect("Blend")("Slider"); | |
| // two states, a & b, can be changed | |
| a = transform.position; | |
| b = thisComp.layer("Target").toWorld([0,0,0]); | |
| linear(blend, 0, 100, a, b) |
| // on scale property | |
| l = thisLayer; // whatever is scaling (could be parent, or shape group, or thisLayer, etc) | |
| value * (100 / l.transform.scale[0]); |
| // requires slider called "Text Reveal" and checkbox called "Toggle" | |
| textReveal = effect("Slider Control")("Text Reveal"); | |
| toggle = effect("Checkbox Control")("Toggle"); | |
| blink = Math.round(time % 1); | |
| pipe = " "; | |
| if (((blink == 1) || (textReveal.speed > 0)) && (toggle == true)) { | |
| pipe = "|" | |
| } | |
| substr(0, textReveal) + pipe; |
| // apply to an elipse's dashes property | |
| dashes = effect("Slider Control")("Slider"); | |
| radius = content("Ellipse").content("Ellipse Path").size[0] / 2; | |
| gap = content("Ellipse").content("Stroke").dash.gap; | |
| segments = dashes <= 0 ? 1 : dashes; | |
| 2 * Math.PI * radius / segments - gap; | |
| // apply to the stroke's offset property | |
| content("Ellipse").content("Stroke").dash.dash / 2; |
| w = content("Rectangle").content("Rectangle Path").size[0]; | |
| h = content("Rectangle").content("Rectangle Path").size[1]; | |
| l = (w * 2) + (h * 2); | |
| l / 60; // adjust as necessary | |
| // adjust offset manually! |
| // create null layer at same position (etc) of object (shift parent) | |
| // unparent the null, and parent the object to the null | |
| // for use in null layer name: ၑ // https://unicode-table.com/en/#1051 || https://unicode-table.com/en/#buginese | |
| // TODO: okay, but what happens after you un-enable? it moves back to neurtral/starting values 🤔 | |
| // on position (for example) of null: | |
| self = OBJECT_LAYER; // set layer here | |
| enabled = self.effect("Parent — Enabled")("Checkbox") == true; | |
| parent = self.effect("Parent — Layer")("Layer"); | |
| if (!enabled) { | |
| value + self.transform.position; | |
| } | |
| else { | |
| // TODO: check if parent hasParent, if so: parent.parent.rotation instead of parent.rotation (eg) | |
| parent.toComp(parent.anchorPoint); | |
| } |
| // apply to opacity property | |
| // requires two sliders to init the start and end point of fade (in z position) | |
| startFade = effect("Start Fade")("Slider"); // 500 | |
| endFade = effect("End Fade")("Slider"); // 3000 | |
| try { | |
| // check if there is a camera | |
| camera = thisComp.activeCamera.toWorld([0,0,0]); | |
| } catch(err) { | |
| // if no camera, assume 50mm | |
| compWidth = thisComp.width * thisComp.pixelAspect; | |
| zPosition = (compWidth / 2)/Math.tan(degreesToRadians(19.799)); | |
| camera = [0,0,-zPosition]; | |
| } | |
| position = toWorld(anchorPoint); | |
| distance = length(camera, position); | |
| linear(distance, startFade, endFade, 100, 0); |
| fade = 1; // fade duration in seconds | |
| fadeIn = (time - inPoint) / fade; | |
| fadeOut = (outPoint - time) / fade; | |
| if (time < inPoint + fade) { | |
| ease(fadeIn, 0, 1) * value; | |
| } else if (time > outPoint - fade) { | |
| ease(fadeOut, 0, 1) * value; | |
| } else { | |
| value; | |
| } |
| // JavaScript For Loop Example - Created by Animoplex: www.animoplex.com | |
| // The JavaScript For Loop and how it can be used in After Effects. | |
| // Full Tutorial: https://www.youtube.com/watch?v=SG3NyHmfc0s&t=91s | |
| myArray = []; | |
| for (i = 0; i < thisComp.numLayers; i++) { | |
| myArray[i] = thisComp.layer(i+1).name; | |
| } | |
| myArray.slice(0, thisComp.numLayers).join("\r") |
| # from @marcusround via the MDA Slack | |
| ffmpeg -i input.avi -filter_complex "split=2 [a][b]; [a] palettegen [pal]; [b] fifo [b]; [b] [pal] paletteuse" output.gif |
| // calculates the length (in pixels) of two positions or points in 2D or 3D space. | |
| // apply to camera's focus distance property | |
| length(pointOfInterest, position) | |
| // link two layers to obj1 and obj2 for their length | |
| obj1 = thisComp.layer("OBJECT 1 LAYER NAME").transform.position; | |
| obj2 = thisComp.layer("OBJECT 2 LAYER NAME").transform.position; | |
| length(obj1, obj2) |
| // apply to the orientation property of a 3D layer | |
| // this example looks at a layer called "Look At Me" | |
| lookAt(thisComp.layer("Look At Me").position, position); |
| // loopIn and loopOut | |
| loopIn() + loopOut() - value; | |
| // works with all loop types | |
| loopIn("pingpong") + loopOut("pingpong") - value; | |
| loopIn("offset") + loopOut("offset") - value; | |
| loopIn("offset") + loopOut("continue") - value; | |
| // and with modifiers | |
| loopIn("pingpong") + loopOut("pingpong", 1) - value; |
| freq = 1; // regular wiggle controls | |
| amp = 110; // regular wiggle controls | |
| octaves = 1; // default value (required for full wiggle expression: no need to edit) | |
| ampMultiplier = 0.5; // default value (required for full wiggle expression: no need to edit) | |
| layerLength = thisLayer.outPoint - thisLayer.inPoint; // length of layer in seconds | |
| loopTime = layerLength; // loop for length of the layer: this can be changed! | |
| t = time % loopTime; // loop reset: every `loopTime` t = 0 | |
| originalWiggle = wiggle(freq, amp, octaves, ampMultiplier, t); | |
| wiggleWiggle = wiggle(freq, amp, octaves, ampMultiplier, t - loopTime); | |
| linear(t, 0, loopTime, originalWiggle, wiggleWiggle) |
| // shift + parent shape layer to text layer | |
| // below is applied to the shape layer's size property | |
| sourceLayer = thisLayer.parent; // change source | |
| sourceLayerWidth = sourceLayer.sourceRectAtTime().width; | |
| sourceLayerHeight = sourceLayer.sourceRectAtTime().height; | |
| sourceLayerScaleX = sourceLayer.transform.scale[0] / 100; | |
| sourceLayerScaleY = sourceLayer.transform.scale[1] / 100; | |
| // requires slider called "Padding" | |
| padding = effect("Padding")("Slider"); | |
| // just width | |
| // x = (sourceLayerWidth + padding) * sourceLayerScaleX; | |
| // y = value[1]; | |
| // just height | |
| // x = value[0]; | |
| // y = (sourceLayerHeight + padding) * sourceLayerScaleY; | |
| // both width and height | |
| x = (sourceLayerWidth + padding) * sourceLayerScaleX; | |
| y = (sourceLayerHeight + padding) * sourceLayerScaleY; | |
| [x,y] | |
| // below is applied to the shape layer's scale property | |
| sourceLayer = thisLayer.parent; // change source | |
| x = value[0] / (sourceLayer.transform.scale[0] / 100); | |
| y = value[1] / (sourceLayer.transform.scale[1] / 100); | |
| [x, y] |
| // give 2D effects or properties the ability to use 3D positioning | |
| // toComp is a layer space transform method that transforms values from a layer space to a composition space. | |
| src = thisComp.layer('Layer 1'); | |
| src.toComp([0,0,0]); |
| // requires a four checkbox controls (named "Top", "Right", "Bottom", "Left") on this layer | |
| // below is applied to this layer's position property | |
| // this keeps the layer in the same visual position | |
| // note this breaks if you separate dimensions (in that case apply individually with no array syntax for `value`) | |
| // additionally, if your layer is scaled from 100% it may not work as expected (todo: fix that) | |
| x = value[0] + transform.anchorPoint[0]; | |
| y = value[1] + transform.anchorPoint[1]; | |
| [x, y] | |
| // below is applied to this layer's anchor point property | |
| sourceLayer = thisLayer.sourceRectAtTime(); | |
| sourceLayerHeight = sourceLayer.height; | |
| sourceLayerWidth = sourceLayer.width; | |
| sourceLayerTop = sourceLayer.top; | |
| sourceLayerLeft = sourceLayer.left; | |
| if ((effect("Top")("Checkbox") == true) && (effect("Left")("Checkbox") == true)) { // top left | |
| x = sourceLayerLeft; | |
| y = sourceLayerTop; | |
| [x,y]; | |
| } else if ((effect("Bottom")("Checkbox") == true) && (effect("Right")("Checkbox") == true)) { // bottom right | |
| x = sourceLayerLeft + sourceLayerWidth; | |
| y = sourceLayerTop + sourceLayerHeight; | |
| [x,y]; | |
| } else if ((effect("Top")("Checkbox") == true) && (effect("Right")("Checkbox") == true)) { // top right | |
| x = sourceLayerLeft + sourceLayerWidth; | |
| y = sourceLayerTop; | |
| [x,y]; | |
| } else if ((effect("Bottom")("Checkbox") == true) && (effect("Left")("Checkbox") == true)) { // bottom left | |
| x = sourceLayerLeft; | |
| y = sourceLayerTop + sourceLayerHeight; | |
| [x,y]; | |
| } else if (effect("Top")("Checkbox") == true) { // top center | |
| x = sourceLayerLeft + (sourceLayerWidth / 2); | |
| y = sourceLayerTop; | |
| [x,y]; | |
| } else if (effect("Right")("Checkbox") == true) { // right center | |
| x = sourceLayerLeft + sourceLayerWidth; | |
| y = sourceLayerTop + (sourceLayerHeight / 2); | |
| [x,y]; | |
| } else if (effect("Bottom")("Checkbox") == true) { // bottom center | |
| x = sourceLayerLeft + (sourceLayerWidth / 2); | |
| y = sourceLayerTop + sourceLayerHeight; | |
| [x,y]; | |
| } else if (effect("Left")("Checkbox") == true) { // left center | |
| x = sourceLayerLeft; | |
| y = sourceLayerTop + (sourceLayerHeight / 2); | |
| [x,y]; | |
| } else [0, 0] |
| // apply to any property to limit its framerate | |
| posterizeTime(5); // 5 fps | |
| valueAtTime(time); |
| // compIndex = thisComp.layer("Comp Index").text.sourceText.value | |
| // or just regular ol' layer index: | |
| seedRandom(index,true); | |
| h = random(); // mess with.. | |
| s = random(); // these values.. | |
| l = random(); // individually | |
| hslToRgb([h,s,l,1]); |
| // similar to positionAnchorPoint.jsx, except meant for a rectangle shape on a shape layer... | |
| // will require checkbox controls (named "Top", "Right", "Bottom", "Left") on this layer | |
| // below goes on Layer > Contents > Rectangle 1 > Transform > Anchor Point | |
| w = content("Rectangle 1").content("Rectangle Path 1").size[0]; | |
| h = content("Rectangle 1").content("Rectangle Path 1").size[1]; | |
| top = effect("Top")("Checkbox") == 1; | |
| right = effect("Right")("Checkbox") == 1;; | |
| bottom = effect("Bottom")("Checkbox") == 1;; | |
| left = effect("Left")("Checkbox") == 1;; | |
| x = 0; | |
| y = 0; | |
| if (top) y = h / -2; | |
| if (right) x = w / 2; | |
| if (bottom) y = h / 2; | |
| if (left) x = w / -2; | |
| [x, y]; | |
| // and this goes on Layer > Transform > Position | |
| x = content("Rectangle 1").transform.anchorPoint[0]; | |
| y = content("Rectangle 1").transform.anchorPoint[1]; | |
| [x, y]; |
| // a few basic ways to manipulate and combine source text with other elements. | |
| // concat strings to source | |
| "(" + text.sourceText + ")" | |
| // countdown example | |
| "T minus " + effect("Controller")("Slider").value + " seconds" |
| # split video file by `n` seconds | |
| # run in terminal | |
| # | |
| # https://unix.stackexchange.com/a/212518 | |
| # set length here | |
| ffmpeg -i input.mp4 -c copy -map 0 -segment_time 00:20:00 -f segment -reset_timestamps 1 output%03d.mp4 | |
| # ^20 minutes! |
| # https://superuser.com/questions/630124/dividing-a-video-clip-by-4-areas | |
| # sub `-acodec copy` for `-an` for no audio! | |
| ffmpeg -i in.mp4 -vf crop=iw/2:ih/2:0:0 -acodec copy v1.mp4 | |
| ffmpeg -i in.mp4 -vf crop=iw/2:ih/2:iw/2:0 -acodec copy v2.mp4 | |
| ffmpeg -i in.mp4 -vf crop=iw/2:ih/2:0:ih/2 -acodec copy v3.mp4 | |
| ffmpeg -i in.mp4 -vf crop=iw/2:ih/2:iw/2:ih/2 -acodec copy v4.mp4 |
| // find out info about another layer's properties and methods | |
| // apply to the `source text` property of an empty text layer, used for debugging | |
| // | |
| // https://helpx.adobe.com/after-effects/using/legacy-and-extend-script-engine.html#reflect-properties-reflect-methods | |
| let obj = thisProperty; // pickwhip property here | |
| let props = []; | |
| do { | |
| Object.getOwnPropertyNames(obj).forEach(prop => { | |
| if (props.indexOf(prop) === -1) { | |
| props.push(prop); | |
| } | |
| }); | |
| } while (obj = Object.getPrototypeOf(obj)); | |
| props.join("\n"); |
| // requires two sliders ("Frequency", "Amplitude").. | |
| freq = effect("Frequency")("Slider"); | |
| amp = effect("Amplitude")("Slider"); | |
| wiggle(freq, amp); | |
| // ..or with checkbox named "Toggle" | |
| toggle = src("Toggle")("Checkbox"); | |
| if (toggle == true) { | |
| freq = effect("Frequency")("Slider"); | |
| amp = effect("Amplitude")("Slider"); | |
| wiggleValue = wiggle(freq, amp); | |
| } else { | |
| wiggleValue = value; | |
| } | |
| // include this if you can make a dropdown to select dimensions (pseudo effect maker/xml) | |
| switch (src("Axis").value) { | |
| case 1: | |
| wiggleValue; | |
| break; | |
| case 2: | |
| [wiggleValue[0], value[1], value[2]]; | |
| break; | |
| case 3: | |
| [value[0], wiggleValue[1], value[2]]; | |
| break; | |
| case 4: | |
| [value[0], value[1], wiggleValue[2]]; | |
| } |