├── .dockerignore ├── .eslintrc ├── .gitattributes ├── .github └── workflows │ └── render-video.yml ├── .gitignore ├── .prettierrc ├── .vscode └── settings.json ├── Dockerfile ├── README.md ├── package-lock.json ├── package.json ├── remotion.config.ts ├── server.tsx ├── src ├── Apple.tsx ├── Arc.tsx ├── AudioSupport.tsx ├── Blue.tsx ├── Both.tsx ├── CodeFrame.tsx ├── Coding.tsx ├── Crash.tsx ├── DefiningComposition.tsx ├── Docs.tsx ├── Editing.tsx ├── EndCard.tsx ├── EndCardRepo.tsx ├── EndCardWebsite.tsx ├── EndCardYarn.tsx ├── Face.tsx ├── FadeOut.tsx ├── GlowingStroke.tsx ├── How.tsx ├── HowRemotionWorks.tsx ├── Intro.tsx ├── Logo │ ├── Logo.tsx │ ├── LogoMask.tsx │ └── Triangle.tsx ├── OpenSource.tsx ├── Outro.tsx ├── Outside.tsx ├── Remotion.tsx ├── Reusability.tsx ├── SSR.tsx ├── Screen.tsx ├── Sequences.tsx ├── Slide.tsx ├── Slide1.tsx ├── SlideTitle.tsx ├── Stairs.tsx ├── Step.tsx ├── Step3.tsx ├── SynchronizedCode.tsx ├── Traditional.tsx ├── TraditionalPrograms.tsx ├── UseFrame.tsx ├── VersionControl.tsx ├── Video.tsx ├── WhatToLearnNext.tsx ├── Why.tsx ├── WrittenInReact.tsx ├── crash.mp3 ├── footage │ ├── P1000318.MP4 │ ├── P1000320.MP4 │ ├── apple-long.mp4 │ ├── codep1-1.mp4 │ ├── codep1-2.mp4 │ ├── codep1-3.mp4 │ ├── codep1-4.mp4 │ ├── codep1-5.mp4 │ ├── codep1-6.mp4 │ ├── codep1-7.mp4 │ ├── codep1-8.mp4 │ ├── codep1-9.mp4 │ ├── editing.mp4 │ ├── how.MP4 │ ├── intro.mp4 │ ├── outro.MP4 │ ├── outside.mp4 │ ├── screen.mp4 │ ├── screen3.mp4 │ ├── split-video.sh │ ├── stairs.mp4 │ ├── traditional.mp4 │ ├── traditional2.mp4 │ └── why.MP4 ├── index.tsx ├── prism.css └── variables.ts └── tsconfig.json /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@remotion" 3 | } 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | src/footage/split-video.sh filter=lfs diff=lfs merge=lfs -text 2 | src/footage/traditional2.mp4 filter=lfs diff=lfs merge=lfs -text 3 | src/footage/codep1-1.mp4 filter=lfs diff=lfs merge=lfs -text 4 | src/footage/how.MP4 filter=lfs diff=lfs merge=lfs -text 5 | src/footage/intro.mp4 filter=lfs diff=lfs merge=lfs -text 6 | src/footage/outro.MP4 filter=lfs diff=lfs merge=lfs -text 7 | src/footage/screen.mp4 filter=lfs diff=lfs merge=lfs -text 8 | src/footage/why.MP4 filter=lfs diff=lfs merge=lfs -text 9 | src/footage/codep1-4.mp4 filter=lfs diff=lfs merge=lfs -text 10 | src/footage/codep1-8.mp4 filter=lfs diff=lfs merge=lfs -text 11 | src/footage/traditional.mp4 filter=lfs diff=lfs merge=lfs -text 12 | src/footage/codep1-6.mp4 filter=lfs diff=lfs merge=lfs -text 13 | src/footage/outside.mp4 filter=lfs diff=lfs merge=lfs -text 14 | src/footage/screen3.mp4 filter=lfs diff=lfs merge=lfs -text 15 | src/footage/codep1-9.mp4 filter=lfs diff=lfs merge=lfs -text 16 | src/footage/P1000318.MP4 filter=lfs diff=lfs merge=lfs -text 17 | src/footage/P1000320.MP4 filter=lfs diff=lfs merge=lfs -text 18 | src/footage/codep1-2.mp4 filter=lfs diff=lfs merge=lfs -text 19 | src/footage/codep1-3.mp4 filter=lfs diff=lfs merge=lfs -text 20 | src/footage/codep1-5.mp4 filter=lfs diff=lfs merge=lfs -text 21 | src/footage/codep1-7.mp4 filter=lfs diff=lfs merge=lfs -text 22 | src/footage/stairs.mp4 filter=lfs diff=lfs merge=lfs -text 23 | src/footage/apple-long.mp4 filter=lfs diff=lfs merge=lfs -text 24 | src/footage/editing.mp4 filter=lfs diff=lfs merge=lfs -text 25 | src/footage/codep1.MP4 filter=lfs diff=lfs merge=lfs -text 26 | src/footage/codep2.MP4 filter=lfs diff=lfs merge=lfs -text 27 | -------------------------------------------------------------------------------- /.github/workflows/render-video.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | inputs: 4 | titleText: 5 | description: "Which text should it say?" 6 | required: true 7 | default: "Welcome to Remotion" 8 | titleColor: 9 | description: "Which color should it be in?" 10 | required: true 11 | default: "black" 12 | name: Render video 13 | jobs: 14 | render: 15 | name: Render video 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@main 19 | - uses: actions/setup-node@main 20 | - run: sudo apt update 21 | - run: sudo apt install ffmpeg 22 | - run: npm i 23 | - run: npm run build -- --props="$WORKFLOW_INPUT" 24 | env: 25 | WORKFLOW_INPUT: ${{ toJson(github.event.inputs) }} 26 | - uses: actions/upload-artifact@v2 27 | with: 28 | name: out.mp4 29 | path: out.mp4 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .DS_Store 4 | src/footage/codep1.MP4 5 | src/footage/codep2.MP4 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "bracketSpacing": false, 4 | "jsxBracketSameLine": false, 5 | "useTabs": true, 6 | "overrides": [ 7 | { 8 | "files": ["*.yml"], 9 | "options": { 10 | "singleQuote": false 11 | } 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.tabSize": 2, 3 | "java.configuration.updateBuildConfiguration": "disabled", 4 | "typescript.tsdk": "node_modules/typescript/lib", 5 | "editor.codeActionsOnSave": { 6 | "source.organizeImports": false, 7 | "source.fixAll": true 8 | }, 9 | "typescript.enablePromptUseWorkspaceTsdk": true 10 | } 11 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # This is a dockerized version of a server that you can easily deploy somewhere. 2 | # If you don't want server rendering, you can safely delete this file. 3 | 4 | FROM node:alpine 5 | 6 | # Installs latest Chromium (85) package. 7 | RUN apk add --no-cache \ 8 | chromium \ 9 | nss \ 10 | freetype \ 11 | freetype-dev \ 12 | harfbuzz \ 13 | ca-certificates \ 14 | ttf-freefont \ 15 | ffmpeg 16 | 17 | # Tell Puppeteer to skip installing Chrome. We'll be using the installed package. 18 | ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \ 19 | PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser 20 | 21 | COPY package*.json ./ 22 | COPY tsconfig.json ./ 23 | COPY src src 24 | COPY *.ts . 25 | COPY *.tsx . 26 | 27 | RUN npm i 28 | 29 | RUN addgroup -S pptruser && adduser -S -g pptruser pptruser \ 30 | && mkdir -p /home/pptruser/Downloads /app \ 31 | && chown -R pptruser:pptruser /home/pptruser \ 32 | && chown -R pptruser:pptruser /app 33 | # Run everything after as non-privileged user. 34 | USER pptruser 35 | 36 | EXPOSE 8000 37 | 38 | CMD ["npm", "run", "server"] 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | My React summit presentation, edited in Remotion! 2 | 3 | Note that because I took the footage directly from the camera, this repo is about 10GB big and will take a long time to clone (need Git LFS). 4 | 5 | I will add a link once the video has been published by React Summit. 6 | 7 | # Remotion video 8 | 9 |

10 | 11 | 12 | 13 |

14 | 15 | Welcome to your Remotion project! 16 | 17 | ## Commands 18 | 19 | **Start Preview** 20 | 21 | ```console 22 | npm start 23 | ``` 24 | 25 | **Render video** 26 | 27 | ```console 28 | npm run build 29 | ``` 30 | 31 | **Server render demo** 32 | 33 | ```console 34 | npm run server 35 | ``` 36 | 37 | See [docs for server-side rendering](https://www.remotion.dev/docs/ssr) here. 38 | 39 | **Upgrade Remotion** 40 | 41 | ```console 42 | npm run upgrade 43 | ``` 44 | 45 | ## Docs 46 | 47 | Get started with Remotion by reading the [fundamentals page](https://www.remotion.dev/docs/the-fundamentals). 48 | 49 | ## Issues 50 | 51 | Found an issue with Remotion? [File an issue here](https://github.com/JonnyBurger/remotion/issues/new). 52 | 53 | ## License 54 | 55 | Notice that a company license is needed. Read [the terms here](https://github.com/JonnyBurger/remotion/blob/main/LICENSE.md). 56 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "remotion-template", 3 | "version": "1.0.0", 4 | "description": "My Remotion video", 5 | "scripts": { 6 | "start": "remotion preview src/index.tsx", 7 | "build": "remotion render src/index.tsx Both both.mp4", 8 | "upgrade": "remotion upgrade", 9 | "server": "ts-node server.tsx", 10 | "test": "eslint src --ext ts,tsx,js,jsx && tsc" 11 | }, 12 | "repository": {}, 13 | "license": "UNLICENSED", 14 | "dependencies": { 15 | "@remotion/babel-loader": "^2.0.0", 16 | "@remotion/bundler": "^2.0.0", 17 | "@remotion/cli": "^2.0.0", 18 | "@remotion/eslint-config": "^2.0.0", 19 | "@remotion/renderer": "^2.0.0", 20 | "@types/express": "^4.17.9", 21 | "@types/react": "^17.0.0", 22 | "@types/styled-components": "^5.1.9", 23 | "eslint": "^7.15.0", 24 | "express": "^4.17.1", 25 | "hack-font": "^3.3.0", 26 | "prettier": "^2.2.1", 27 | "prettier-plugin-organize-imports": "^1.1.1", 28 | "prism-react-renderer": "^1.2.0", 29 | "react": "^17.0.1", 30 | "react-dom": "^17.0.2", 31 | "remotion": "^2.0.0", 32 | "styled-components": "^5.2.1", 33 | "ts-node": "^9.1.1", 34 | "typescript": "^4.1.3" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /remotion.config.ts: -------------------------------------------------------------------------------- 1 | import {Config} from 'remotion'; 2 | 3 | Config.Output.setCodec('h264'); 4 | Config.Output.setImageSequence(false); 5 | Config.Rendering.setImageFormat('jpeg'); 6 | -------------------------------------------------------------------------------- /server.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This is an example of a server that returns dynamic video. 3 | * Run `npm run server` to try it out! 4 | * If you don't want to render videos on a server, you can safely 5 | * delete this file. 6 | */ 7 | 8 | import {bundle} from '@remotion/bundler'; 9 | import { 10 | getCompositions, 11 | renderFrames, 12 | stitchFramesToVideo, 13 | } from '@remotion/renderer'; 14 | import express from 'express'; 15 | import fs from 'fs'; 16 | import os from 'os'; 17 | import path from 'path'; 18 | 19 | const app = express(); 20 | const port = 8000; 21 | const compositionId = 'HelloWorld'; 22 | 23 | const cache = new Map(); 24 | 25 | app.get('/', async (req, res) => { 26 | const sendFile = (file: string) => { 27 | fs.createReadStream(file) 28 | .pipe(res) 29 | .on('close', () => { 30 | res.end(); 31 | }); 32 | }; 33 | try { 34 | if (cache.get(JSON.stringify(req.query))) { 35 | sendFile(cache.get(JSON.stringify(req.query)) as string); 36 | return; 37 | } 38 | const bundled = await bundle(path.join(__dirname, './src/index.tsx')); 39 | const comps = await getCompositions(bundled); 40 | const video = comps.find((c) => c.id === compositionId); 41 | if (!video) { 42 | throw new Error(`No video called ${compositionId}`); 43 | } 44 | res.set('content-type', 'video/mp4'); 45 | 46 | const tmpDir = await fs.promises.mkdtemp( 47 | path.join(os.tmpdir(), 'remotion-') 48 | ); 49 | await renderFrames({ 50 | config: video, 51 | webpackBundle: bundled, 52 | onStart: () => console.log('Rendering frames...'), 53 | onFrameUpdate: (f) => { 54 | if (f % 10 === 0) { 55 | console.log(`Rendered frame ${f}`); 56 | } 57 | }, 58 | parallelism: null, 59 | outputDir: tmpDir, 60 | userProps: req.query, 61 | compositionId, 62 | imageFormat: 'jpeg', 63 | }); 64 | 65 | const finalOutput = path.join(tmpDir, 'out.mp4'); 66 | await stitchFramesToVideo({ 67 | dir: tmpDir, 68 | force: true, 69 | fps: video.fps, 70 | height: video.height, 71 | width: video.width, 72 | outputLocation: finalOutput, 73 | imageFormat: 'jpeg', 74 | }); 75 | cache.set(JSON.stringify(req.query), finalOutput); 76 | sendFile(finalOutput); 77 | console.log('Video rendered and sent!'); 78 | } catch (err) { 79 | console.error(err); 80 | res.json({ 81 | error: err, 82 | }); 83 | } 84 | }); 85 | 86 | app.listen(port); 87 | 88 | console.log( 89 | [ 90 | `The server has started on http://localhost:${port}!`, 91 | 'You can render a video by passing props as URL parameters.', 92 | '', 93 | 'If you are running Hello World, try this:', 94 | '', 95 | `http://localhost:${port}?titleText=Hello,+World!&titleColor=red`, 96 | '', 97 | ].join('\n') 98 | ); 99 | -------------------------------------------------------------------------------- /src/Apple.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | interpolate, 4 | spring, 5 | useCurrentFrame, 6 | useVideoConfig, 7 | Video, 8 | } from 'remotion'; 9 | import apple from './footage/apple-long.mp4'; 10 | 11 | export const Apple: React.FC = () => { 12 | const frame = useCurrentFrame(); 13 | const {durationInFrames, fps} = useVideoConfig(); 14 | const opacity = interpolate( 15 | frame, 16 | [durationInFrames - 10, durationInFrames], 17 | [1, 0] 18 | ); 19 | const scale = spring({ 20 | frame, 21 | fps, 22 | config: { 23 | damping: 400, 24 | mass: 2, 25 | }, 26 | }); 27 | return ( 28 |
37 |
43 | ); 44 | }; 45 | -------------------------------------------------------------------------------- /src/Arc.tsx: -------------------------------------------------------------------------------- 1 | import {interpolate, spring, useCurrentFrame, useVideoConfig} from 'remotion'; 2 | 3 | export const Arc: React.FC<{ 4 | rotation: number; 5 | delay: number; 6 | }> = ({delay, rotation}) => { 7 | const frame = useCurrentFrame(); 8 | const {height, width, fps} = useVideoConfig(); 9 | const rx = 180; 10 | const ry = 400; 11 | const arcLength = Math.PI * 2 * Math.sqrt((rx * rx + ry * ry) / 2); 12 | 13 | const progress = spring({ 14 | frame: frame - delay, 15 | fps, 16 | config: { 17 | damping: 100, 18 | mass: 10, 19 | }, 20 | }); 21 | 22 | const opacity = interpolate(progress, [0, 0.2], [0, 0.7], { 23 | extrapolateRight: 'clamp', 24 | extrapolateLeft: 'clamp', 25 | }); 26 | 27 | const strokeWidth = interpolate(progress, [0, 1], [200, 60]); 28 | 29 | return ( 30 | 37 | 51 | 52 | ); 53 | }; 54 | -------------------------------------------------------------------------------- /src/AudioSupport.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {interpolate, spring, useCurrentFrame, useVideoConfig} from 'remotion'; 3 | import {Slide} from './Slide'; 4 | import {SlideSubtitle, SlideTitle} from './SlideTitle'; 5 | 6 | export const AudioSupport: React.FC = () => { 7 | const frame = useCurrentFrame(); 8 | const {fps, durationInFrames} = useVideoConfig(); 9 | const pos = spring({ 10 | fps, 11 | frame, 12 | config: { 13 | damping: 200, 14 | }, 15 | }); 16 | const posout = spring({ 17 | fps, 18 | frame: durationInFrames - frame - 10, 19 | config: { 20 | damping: 200, 21 | }, 22 | }); 23 | const actualpos = 24 | interpolate(pos, [0, 1], [1200, 0]) + 25 | interpolate(posout, [0, 1], [-1200, 0]); 26 | return ( 27 | 28 |
29 | <Audio/> 30 | 31 |
    32 |
  • Include audio in your video
  • 33 |
  • Use multiple tracks, cut and trim, control volume
  • 34 |
  • Remotion will place and mix the audio for you
  • 35 |
36 |
37 |
38 |
39 | ); 40 | }; 41 | -------------------------------------------------------------------------------- /src/Blue.tsx: -------------------------------------------------------------------------------- 1 | import {AbsoluteFill} from 'remotion'; 2 | 3 | export const Blue: React.FC = () => { 4 | return ( 5 | 11 | ); 12 | }; 13 | -------------------------------------------------------------------------------- /src/Both.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Face} from './Face'; 3 | import {ScreenRecording} from './Screen'; 4 | 5 | export const Both: React.FC = () => { 6 | return ( 7 |
8 |
17 | 18 |
19 |
27 | 28 |
29 |
30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /src/CodeFrame.tsx: -------------------------------------------------------------------------------- 1 | import 'hack-font/build/web/hack.css'; 2 | import Highlight, {defaultProps} from 'prism-react-renderer'; 3 | import {interpolate, spring, useCurrentFrame, useVideoConfig} from 'remotion'; 4 | import styled from 'styled-components'; 5 | import './prism.css'; 6 | 7 | const Pre = styled.pre<{ 8 | width: number; 9 | }>` 10 | text-align: left; 11 | margin: 1em 0; 12 | font-size: 40px; 13 | width: ${(props) => props.width}px; 14 | `; 15 | 16 | const Line = styled.div` 17 | display: table-row; 18 | `; 19 | 20 | const LineContent = styled.span` 21 | display: table-cell; 22 | `; 23 | 24 | const Container = styled.div` 25 | flex: 1; 26 | justify-content: center; 27 | align-items: center; 28 | display: flex; 29 | `; 30 | 31 | const Frame = styled.div` 32 | border: 2px solid rgba(0, 0, 0, 0.14); 33 | border-radius: 20px; 34 | background-color: white; 35 | 36 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, 37 | Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 38 | `; 39 | 40 | const Title = styled.div` 41 | padding: 20px; 42 | text-align: center; 43 | font-size: 28px; 44 | `; 45 | 46 | const getProgressOfLine = ({ 47 | line, 48 | frame, 49 | fps, 50 | timing, 51 | }: { 52 | line: number; 53 | frame: number; 54 | fps: number; 55 | timing: Timing[]; 56 | }) => { 57 | const segment = timing.find((t) => t.line === line); 58 | if (!segment) { 59 | return 1; 60 | } 61 | return spring({ 62 | fps, 63 | frame: frame - segment.from, 64 | config: { 65 | stiffness: 200, 66 | damping: 100, 67 | mass: 0.5, 68 | overshootClamping: true, 69 | }, 70 | }); 71 | }; 72 | 73 | type Timing = { 74 | line: number; 75 | from: number; 76 | }; 77 | 78 | export const CodeFrame: React.FC<{ 79 | code: string; 80 | timing: Timing[]; 81 | title: string; 82 | width: number; 83 | }> = ({code, timing, title, width}) => { 84 | const frame = useCurrentFrame(); 85 | const {fps} = useVideoConfig(); 86 | 87 | return ( 88 | 89 | 90 | {title} 91 |
92 | 98 | {({className, style, tokens, getLineProps, getTokenProps}) => ( 99 |
100 | 								{tokens.map((line, i) => {
101 | 									return (
102 | 										
132 | 											
133 | 												{line.map((token, key) => {
134 | 													const props = getTokenProps({token, key});
135 | 													return (
136 | 														
145 | 													);
146 | 												})}
147 | 											
148 | 										
149 | 									);
150 | 								})}
151 | 							
152 | )} 153 |
154 |
155 | 156 |
157 | ); 158 | }; 159 | -------------------------------------------------------------------------------- /src/Coding.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | AbsoluteFill, 3 | interpolate, 4 | Sequence, 5 | spring, 6 | useCurrentFrame, 7 | useVideoConfig, 8 | } from 'remotion'; 9 | import {SynchronizedCode} from './SynchronizedCode'; 10 | const topick = [ 11 | [-22250, 22736], 12 | [-25005, 27426], 13 | [-46740, 52920], 14 | [-53503, 69746], 15 | [-72858, 75706], 16 | [-87294, 96643], 17 | [-98325, 100068], 18 | [-105244, 108610], 19 | ]; 20 | 21 | export const CODING_DURATION = topick.reduce((a, b) => { 22 | return a + b[0] + b[1]; 23 | }, 0); 24 | 25 | export const Coding: React.FC<{ 26 | type: 'both' | 'face' | 'screen'; 27 | }> = ({type}) => { 28 | let durationSum = 0; 29 | const {fps, durationInFrames} = useVideoConfig(); 30 | const frame = useCurrentFrame(); 31 | const progress = spring({ 32 | fps, 33 | frame, 34 | config: { 35 | damping: 200, 36 | }, 37 | }); 38 | const translateX = 39 | type === 'screen' ? interpolate(progress, [0, 1], [2000, 0]) : 0; 40 | 41 | const opacity = interpolate( 42 | frame, 43 | [durationInFrames - 20, durationInFrames], 44 | [1, 0] 45 | ); 46 | 47 | return ( 48 | 54 | {topick.map((pick) => { 55 | const duration = pick[0] + pick[1]; 56 | durationSum += duration; 57 | return ( 58 | 59 | 60 | 61 | 62 | 63 | ); 64 | })} 65 | 66 | ); 67 | }; 68 | -------------------------------------------------------------------------------- /src/Crash.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | Img, 4 | interpolate, 5 | spring, 6 | useCurrentFrame, 7 | useVideoConfig, 8 | } from 'remotion'; 9 | import {Slide} from './Slide'; 10 | 11 | export const Crash: React.FC = () => { 12 | const frame = useCurrentFrame(); 13 | const {fps} = useVideoConfig(); 14 | const scale = spring({ 15 | frame, 16 | fps, 17 | }); 18 | 19 | const hang = interpolate( 20 | spring({ 21 | frame: frame - 100, 22 | fps, 23 | config: { 24 | mass: 10, 25 | damping: 20, 26 | }, 27 | }), 28 | [0, 1], 29 | [0, -90] 30 | ); 31 | 32 | const fallDown = spring({ 33 | fps, 34 | frame: frame - 200, 35 | config: { 36 | damping: 200, 37 | }, 38 | }); 39 | 40 | return ( 41 | 42 |
48 | 52 |
53 |
54 | ); 55 | }; 56 | -------------------------------------------------------------------------------- /src/DefiningComposition.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {CodeFrame} from './CodeFrame'; 3 | import {Slide} from './Slide'; 4 | 5 | export const DefiningComposition: React.FC = () => { 6 | return ( 7 | 8 | { 13 | return ( 14 | 22 | ); 23 | } 24 | 25 | `.trim()} 26 | timing={[ 27 | { 28 | line: 4, 29 | from: 800, 30 | }, 31 | { 32 | line: 5, 33 | from: 850, 34 | }, 35 | { 36 | line: 6, 37 | from: 900, 38 | }, 39 | { 40 | line: 7, 41 | from: 950, 42 | }, 43 | { 44 | line: 8, 45 | from: 1100, 46 | }, 47 | ]} 48 | /> 49 | 50 | ); 51 | }; 52 | -------------------------------------------------------------------------------- /src/Docs.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {interpolate, spring, useCurrentFrame, useVideoConfig} from 'remotion'; 3 | import {Slide} from './Slide'; 4 | import {SlideSubtitle, SlideTitle} from './SlideTitle'; 5 | 6 | export const Docs: React.FC = () => { 7 | const frame = useCurrentFrame(); 8 | const {fps, durationInFrames} = useVideoConfig(); 9 | const pos = spring({ 10 | fps, 11 | frame, 12 | config: { 13 | damping: 200, 14 | }, 15 | }); 16 | const posout = spring({ 17 | fps, 18 | frame: durationInFrames - frame - 10, 19 | config: { 20 | damping: 200, 21 | }, 22 | }); 23 | const actualpos = 24 | interpolate(pos, [0, 1], [1200, 0]) + 25 | interpolate(posout, [0, 1], [-1200, 0]); 26 | return ( 27 | 28 |
29 | Docs: remotion.dev 30 | 31 |
    32 |
  • Documentation of all Remotion APIs
  • 33 |
  • Tips for common use cases
  • 34 |
  • Video tutorials
  • 35 |
36 |
37 |
38 |
39 | ); 40 | }; 41 | -------------------------------------------------------------------------------- /src/Editing.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | AbsoluteFill, 4 | interpolate, 5 | spring, 6 | useCurrentFrame, 7 | useVideoConfig, 8 | Video, 9 | } from 'remotion'; 10 | import styled from 'styled-components'; 11 | import editing from './footage/editing.mp4'; 12 | import {Slide} from './Slide'; 13 | 14 | const Banner = styled.div` 15 | background-color: white; 16 | left: 60px; 17 | font-size: 60px; 18 | font-weight: 500; 19 | font-family: --apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, 20 | Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 21 | padding: 20px 40px; 22 | display: inline-block; 23 | margin-left: 100px; 24 | `; 25 | 26 | export const Editing: React.FC = () => { 27 | const frame = useCurrentFrame(); 28 | const {fps} = useVideoConfig(); 29 | const translation = spring({ 30 | fps, 31 | frame: frame - 20, 32 | config: { 33 | damping: 200, 34 | }, 35 | }); 36 | 37 | return ( 38 | 39 | 57 | ); 58 | }; 59 | -------------------------------------------------------------------------------- /src/EndCard.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | AbsoluteFill, 3 | interpolate, 4 | Sequence, 5 | spring, 6 | useCurrentFrame, 7 | useVideoConfig, 8 | } from 'remotion'; 9 | import styled from 'styled-components'; 10 | import {EndCardRepo} from './EndCardRepo'; 11 | import {EndCardWebsite} from './EndCardWebsite'; 12 | import {EndCardYarn} from './EndCardYarn'; 13 | import {GlowingStroke} from './GlowingStroke'; 14 | import {LogoMask} from './Logo/LogoMask'; 15 | 16 | const Outer = styled(AbsoluteFill)` 17 | background-color: white; 18 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.4); 19 | `; 20 | 21 | const Container = styled.div` 22 | display: flex; 23 | flex: 1; 24 | flex-direction: row; 25 | padding: 40px; 26 | `; 27 | 28 | const Left = styled.div` 29 | flex: 1; 30 | display: flex; 31 | `; 32 | 33 | const Right = styled.div` 34 | flex: 1; 35 | display: flex; 36 | flex-direction: column; 37 | `; 38 | 39 | const RADIUS = 30; 40 | 41 | const Panel = styled.div` 42 | position: absolute; 43 | background-color: white; 44 | border-radius: ${RADIUS}px; 45 | display: flex; 46 | justify-content: center; 47 | align-items: center; 48 | box-shadow: 0 15px 20px rgba(0, 0, 0, 0.07); 49 | `; 50 | 51 | const Centered = styled(AbsoluteFill)` 52 | justify-content: center; 53 | align-items: center; 54 | `; 55 | 56 | export const EndCard: React.FC = () => { 57 | const width = 1792; 58 | const height = 1120; 59 | const {fps, durationInFrames} = useVideoConfig(); 60 | 61 | const PADDING = 40; 62 | const SPACING = 30; 63 | const PANEL_WIDTH = (width - PADDING * 2 - SPACING) / 2; 64 | const BIG_PANEL_HEIGHT = height - PADDING * 2; 65 | const SMALL_PANEL_HEIGHT = (height - PADDING * 2 - SPACING) / 2; 66 | const frame = useCurrentFrame(); 67 | const progress = (i: number) => 68 | spring({ 69 | fps, 70 | frame: frame - i * 10 - 15, 71 | config: { 72 | damping: 100, 73 | mass: 2, 74 | }, 75 | }); 76 | 77 | const opacity = interpolate(frame, [0, 50], [0, 1]); 78 | 79 | return ( 80 | 81 | 82 | 83 | 84 | 91 | 92 | 100 | 101 | 102 | 103 | 104 |
105 | 106 | 113 | 114 | 122 | 123 | 124 | 125 |
126 | 134 | 135 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | ); 155 | }; 156 | -------------------------------------------------------------------------------- /src/EndCardRepo.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Container = styled.div` 4 | display: flex; 5 | justify-content: center; 6 | align-items: center; 7 | flex-direction: column; 8 | `; 9 | 10 | const Title = styled.div` 11 | font-weight: 700; 12 | font-family: -apple-system, BlinkMacSystemFont; 13 | font-size: 30px; 14 | margin-bottom: 7px; 15 | `; 16 | 17 | const Link = styled.div` 18 | font-weight: 700; 19 | font-size: 40px; 20 | font-family: -apple-system, BlinkMacSystemFont; 21 | background: linear-gradient(to right, #f5ad43, #fd764a); 22 | -webkit-background-clip: text; 23 | -moz-background-clip: text; 24 | background-clip: text; 25 | -webkit-text-fill-color: transparent; 26 | -moz-text-fill-color: transparent; 27 | text-fill-color: transparent; 28 | `; 29 | 30 | export const EndCardRepo: React.FC = () => { 31 | return ( 32 | 33 | This talk is on Github: 34 | 39 | JonnyBurger/react-summit-talk 40 | 41 | 42 | ); 43 | }; 44 | -------------------------------------------------------------------------------- /src/EndCardWebsite.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Container = styled.div` 4 | display: flex; 5 | justify-content: center; 6 | align-items: center; 7 | flex-direction: column; 8 | `; 9 | 10 | const Title = styled.div` 11 | font-weight: 700; 12 | font-family: -apple-system, BlinkMacSystemFont; 13 | font-size: 30px; 14 | margin-bottom: 5px; 15 | `; 16 | 17 | const Link = styled.div` 18 | font-weight: 700; 19 | font-size: 60px; 20 | font-family: -apple-system, BlinkMacSystemFont; 21 | background: linear-gradient(to right, #e01d67, #79367a); 22 | -webkit-background-clip: text; 23 | -moz-background-clip: text; 24 | background-clip: text; 25 | -webkit-text-fill-color: transparent; 26 | -moz-text-fill-color: transparent; 27 | text-fill-color: transparent; 28 | `; 29 | 30 | export const EndCardWebsite: React.FC = () => { 31 | return ( 32 | 33 | Read the documentation: 34 | 41 | remotion.dev 42 | 43 | 44 | ); 45 | }; 46 | -------------------------------------------------------------------------------- /src/EndCardYarn.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import {Triangle} from './Logo/Triangle'; 3 | 4 | const Container = styled.div` 5 | justify-content: center; 6 | align-items: center; 7 | display: flex; 8 | flex-direction: column; 9 | `; 10 | 11 | const Title = styled.div` 12 | font-weight: 700; 13 | font-family: -apple-system, BlinkMacSystemFont; 14 | font-size: 30px; 15 | margin-bottom: 5px; 16 | `; 17 | 18 | const YarnCreateVideo = styled.div` 19 | font-weight: 700; 20 | font-size: 60px; 21 | font-family: -apple-system, BlinkMacSystemFont; 22 | background: linear-gradient(to right, #4290f5, #42e9f5); 23 | -webkit-background-clip: text; 24 | -moz-background-clip: text; 25 | background-clip: text; 26 | -webkit-text-fill-color: transparent; 27 | -moz-text-fill-color: transparent; 28 | text-fill-color: transparent; 29 | `; 30 | 31 | export const EndCardYarn: React.FC = () => { 32 | return ( 33 | 34 |
35 | 36 |
37 |
38 |
39 | Create your first video: 40 | yarn create video 41 |
42 | ); 43 | }; 44 | -------------------------------------------------------------------------------- /src/Face.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {AbsoluteFill, Audio, Sequence, useVideoConfig} from 'remotion'; 3 | import {Coding, CODING_DURATION} from './Coding'; 4 | import crash from './crash.mp3'; 5 | import {How, howlength} from './How'; 6 | import {Intro} from './Intro'; 7 | import {LogoMask} from './Logo/LogoMask'; 8 | import {Outro, outrolength} from './Outro'; 9 | import {Outside} from './Outside'; 10 | import {Stairs} from './Stairs'; 11 | import {Traditional} from './Traditional'; 12 | import { 13 | introlength, 14 | introoffset, 15 | outsidelength, 16 | stairslength, 17 | traditionallength, 18 | } from './variables'; 19 | import {ENABLE_WHY, Why, whylength} from './Why'; 20 | 21 | export const Face: React.FC = () => { 22 | const {durationInFrames} = useVideoConfig(); 23 | return ( 24 | <> 25 | 26 | 27 | 28 | 32 | 33 | 34 | 38 | 39 | 40 | 44 | 45 | 46 | 56 | 57 | 58 | {ENABLE_WHY ? ( 59 | 71 | 72 | 73 | ) : ( 74 | 0 75 | )} 76 | 88 | 89 | 90 | 103 | 104 | 105 | 106 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | ); 115 | }; 116 | -------------------------------------------------------------------------------- /src/FadeOut.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | AbsoluteFill, 4 | interpolate, 5 | spring, 6 | useCurrentFrame, 7 | useVideoConfig, 8 | } from 'remotion'; 9 | import {Triangle} from './Logo/Triangle'; 10 | 11 | export const FadeOut: React.FC = () => { 12 | const frame = useCurrentFrame(); 13 | const {fps} = useVideoConfig(); 14 | const spring1 = spring({ 15 | fps, 16 | frame, 17 | config: {damping: 200, mass: 1.5}, 18 | }); 19 | const size1 = interpolate(spring1, [0, 1], [0, 10]); 20 | const spring2 = spring({ 21 | fps, 22 | frame: frame - 15, 23 | config: {damping: 200, mass: 1.5}, 24 | }); 25 | const size2 = interpolate(spring2, [0, 1], [0, 10]); 26 | return ( 27 |
32 | 33 |
40 | 41 |
42 |
43 | 44 |
51 | 52 |
53 |
54 |
55 | ); 56 | }; 57 | -------------------------------------------------------------------------------- /src/GlowingStroke.tsx: -------------------------------------------------------------------------------- 1 | import {interpolate, spring, useCurrentFrame, useVideoConfig} from 'remotion'; 2 | import styled from 'styled-components'; 3 | 4 | const Container = styled.div<{ 5 | width: number; 6 | height: number; 7 | }>` 8 | position: absolute; 9 | width: ${(props) => props.width}px; 10 | height: ${(props) => props.height}px; 11 | `; 12 | 13 | export const GlowingStroke: React.FC<{ 14 | width: number; 15 | height: number; 16 | radius: number; 17 | color1: string; 18 | color2: string; 19 | offset: number; 20 | }> = ({width, height, radius, color1, color2, offset}) => { 21 | const frame = useCurrentFrame(); 22 | const {fps} = useVideoConfig(); 23 | const start = 40 + offset; 24 | const progress = spring({ 25 | frame: frame - start, 26 | fps, 27 | config: { 28 | damping: 200, 29 | stiffness: 50, 30 | mass: 6, 31 | }, 32 | }); 33 | const circumference = width * 2 + height * 2; 34 | const strokeDashoffset = interpolate( 35 | progress, 36 | [0, 1], 37 | [0, -circumference + 250] 38 | ); 39 | const opacity = (() => { 40 | if (frame < start + 10) { 41 | return interpolate(frame, [start, start + 10], [0, 1], { 42 | extrapolateLeft: 'clamp', 43 | extrapolateRight: 'clamp', 44 | }); 45 | } 46 | return interpolate(progress, [0.9, 0.95], [1, 0]); 47 | })(); 48 | const gId = `g-${color1}-${color2}`; 49 | return ( 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 75 | 76 | 77 | ); 78 | }; 79 | -------------------------------------------------------------------------------- /src/How.tsx: -------------------------------------------------------------------------------- 1 | import {interpolate, Sequence, useCurrentFrame, Video} from 'remotion'; 2 | import how from './footage/how.mp4'; 3 | export const howlength = 241.5 * 50; 4 | const howoffset = -400; 5 | 6 | export const How: React.FC = () => { 7 | const frame = useCurrentFrame(); 8 | const opacity = interpolate(frame, [0, 50], [0, 1]); 9 | return ( 10 | 15 | 17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /src/HowRemotionWorks.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {interpolate, useCurrentFrame} from 'remotion'; 3 | import {Slide} from './Slide'; 4 | import {SlideTitle} from './SlideTitle'; 5 | 6 | export const HowRemotionWorks: React.FC = () => { 7 | const frame = useCurrentFrame(); 8 | const opacity = interpolate(frame, [0, 50], [0, 1]); 9 | return ( 10 | 11 | 12 | Understanding Remotion 13 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /src/Intro.tsx: -------------------------------------------------------------------------------- 1 | import {Video} from 'remotion'; 2 | import intro from './footage/intro.mp4'; 3 | 4 | export const Intro: React.FC = () => { 5 | return