Norsk voiceover, musikk og animasjon, uten designer, uten filming og uten After Effects. Dette er den nøyaktige arbeidsflyten vi bruker, med malene vi kopierer fra hver gang. Hver regel her finnes fordi noe gikk galt første gang.
Skriver manus, planlegger scenene og koder hele animasjonen.
Gjør React-kode om til MP4. Én fil gir både 9:16 og 1:1.
Norsk stemme med tidsstempel for hver eneste bokstav.
Et spor laget til nøyaktig lengde og stemning, ingen lisensjakt.
Tale til tekst. Sjekker at stemmen faktisk sa det som står i manus.
Riktig lydnivå, stillbilder til kontroll og siste finpuss.
Stemmen lages først, med tidsstempler. Så låses hver eneste animasjon til ordet den hører til. Det er derfor tekst, bilde og lyd treffer helt presist, uten at noen sitter og justerer i et redigeringsprogram.
Rekkefølgen er viktig. Mange starter med animasjonen og legger på stemme til slutt. Vi gjør det motsatt, fordi stemmen bestemmer tempoet i alt annet.
Bestem først om det er en annonse for kald trafikk eller en merkevarefilm. Pen typografi på en tom bakgrunn stopper ingen i feeden.
Hvorfor: Mange korte setninger får AI-stemmen til å starte tonefallet på nytt hver gang. Det er det som gjør at den høres hakkete ut, ikke pausene.
Hvorfor: Vi prøvde å speede opp lydfila i etterkant én gang. Alle hørte det med en gang, selv om ingen kunne si hva som var galt.
Hvorfor: Når musikken skifter i samme øyeblikk som løsningen kommer, føles hele annonsen gjennomtenkt. Seeren merker det uten å vite hvorfor.
Sikker sone i 9:16. All tekst ligger mellom y=400 og y=1520 på en 1080×1920-video. Reels, Stories og TikTok dekker toppen og bunnen med knapper og tekst. Bakgrunn og bilder kan gå helt ut, og teksten skal være 25 til 30 prosent større enn i 1:1.
Stemmen skal alltid ligge over. Musikken er et teppe.
Hvorfor jevnt: Senker du musikken for hver setning, pumper den opp i hver lille pause. Da virker stemmen hakkete selv om den ikke er det. Til slutt normaliseres hele videoen til -15 LUFS.
Av alt vi har testet er dette formatet det som har fått best respons. Rundt 45 sekunder, bygget slik:
Ikke «dårlig kommunikasjon», men chatten som flommer over til den viktige beskjeden forsvinner ut av skjermen. Ikke «tidkrevende», men klokka som går fra 21:47 til 23:12.
Problem 3 får løsning 3. Seeren trenger ikke tenke, bare kjenne igjen.
Over hver løsning står problemet med rød strek: rapport full av klikk. Da ser man direkte hva som er fikset.
Dette er de samme malene vi selv starter fra. Du trenger en ElevenLabs-konto og en Mac eller PC med Node og Python.
Lag en motion graphic-annonse i Remotion for [bedrift].
Målgruppe: [hvem]. Tilbud: [hva]. Nettside: [url].
Hent alle påstander fra nettsiden, ikke finn på tall.
Format: problem og løsning, ca. 45 sekunder, 30 fps, 9:16 og 1:1 fra samme komponent.
- 5 konkrete problemer som små scener (ikke ikonlister), deretter et vendepunkt
der logoen tegner seg, deretter 5 løsninger i samme rekkefølge.
Over hver løsning: det gamle problemet strøket over i rødt.
- Manus: få, sammenhengende setninger. Forkortelser med bindestrek. Valuta skrevet ut.
- Stemme: ElevenLabs eleven_v4, language_code "no", endepunktet with-timestamps.
Lag 4 takes, sjekk hver med tale til tekst mot manus,
velg den med riktig tekst og jevnest tempo.
- Musikk: ElevenLabs Music i to halvdeler (urolig moll, så varm dur),
skiftet på vendepunktet.
- Bygg en scenetabell i frames øverst i fila, låst til ordtidene fra stemmen.
- All tekst i 9:16 mellom y=400 og y=1520.
- Lydmiks: stemme 0.95, musikk 0.30 senket jevnt til 0.09 under hele stemmen.
- Render begge formater og normaliser til -15 LUFS.
Vis meg manus og scenetabell før du genererer stemme.{
"model_id": "eleven_v4",
"language_code": "no",
"voice_settings": {
"stability": 0.5,
"similarity_boost": 0.78,
"style": 0.0,
"use_speaker_boost": true,
"speed": 1.0
}
}import json, base64
r = json.load(open("resp.json"))
open("vo.mp3", "wb").write(base64.b64decode(r["audio_base64"]))
a = r.get("alignment") or r["normalized_alignment"]
chars = a["characters"]
starts = a["character_start_times_seconds"]
ends = a["character_end_times_seconds"]
words, cur, t0, prev = [], "", None, 0
for c, s, e in zip(chars, starts, ends):
if c.strip() == "":
if cur:
words.append({"word": cur, "start": round(t0 * 30), "end": round(prev * 30)})
cur, t0 = "", None
else:
if t0 is None:
t0 = s
cur += c
prev = e
if cur:
words.append({"word": cur, "start": round(t0 * 30), "end": round(prev * 30)})
json.dump(words, open("words.json", "w"), ensure_ascii=False, indent=1){
"prompt": "Instrumental bed for a 47 second Scandinavian software ad in two halves. First 20 seconds: sparse, slightly tense and restless, muted plucked strings and a ticking clock-like pulse, minor key, unresolved. Then at 20 seconds a clear warm turn: soft felt piano and gentle brushed percussion, major key, hopeful, calm, building subtly to a clean resolved ending. 96 BPM, no vocals, no drops, understated.",
"music_length_ms": 47000,
"force_instrumental": true
}import { Easing, interpolate } from "remotion";
const clamp = { extrapolateLeft: "clamp", extrapolateRight: "clamp" } as const;
// Synlig fra frame a til b: 16 frames inn, 14 frames ut
export const inOut = (f: number, a: number, b: number, fi = 16, fo = 14) =>
Math.min(
interpolate(f, [a, a + fi], [0, 1], { ...clamp, easing: Easing.out(Easing.exp) }),
interpolate(f, [b - fo, b], [1, 0], clamp)
);import json, subprocess, sys
src, dst = sys.argv[1], sys.argv[2]
o = subprocess.run(
["ffmpeg", "-hide_banner", "-i", src, "-af",
"loudnorm=I=-15:TP=-1.5:LRA=11:print_format=json", "-f", "null", "-"],
capture_output=True, text=True).stderr
d = json.loads(o[o.rindex("{"):o.rindex("}") + 1])
af = (f"loudnorm=I=-15:TP=-1.5:LRA=11:measured_I={d['input_i']}:measured_TP={d['input_tp']}"
f":measured_LRA={d['input_lra']}:measured_thresh={d['input_thresh']}"
f":offset={d['target_offset']}:linear=true")
subprocess.run(["ffmpeg", "-y", "-i", src, "-c:v", "copy", "-af", af,
"-ar", "48000", "-c:a", "aac", "-b:a", "192k", dst], check=True)Har du laget hele animasjonen og vil teste en annen stemme? Match ordene i gammel og ny versjon, lagre to lister med frame-tall (OLD og NEW), og la komposisjonen oversette hver frame til «gammel tid». Da flytter hele animasjonen seg ord for ord etter den nye stemmen. Vi tester flere stemmer på samme video på noen minutter på denne måten.
import { interpolate, useCurrentFrame } from "remotion";
const extend = { extrapolateLeft: "extend", extrapolateRight: "extend" } as const;
const toOld = (f: number) => interpolate(f, NEW, OLD, extend);
// Bruk denne i stedet for useCurrentFrame() i hele komposisjonen
export const useWarpedFrame = () => toOld(useCurrentFrame());0 av 7 sjekket

Hei, Victor her. Dette er den samme oppskriften vi bruker når vi lager motion-annonser for kundene våre. Den blir oppdatert hver gang vi lærer noe nytt, så lagre lenken.
Vil du heller at vi lager annonsene for deg? Send oss nettsiden din nederst på siden, så får du et gratis forslag.

Tallene er hentet fra kundenes egne annonsekontoer.
Se også: Google Ads-byrå · Meta Ads-byrå · Performance marketing-byrå · Hva er UGC? · Google Ads-byrå Oslo
Send oss nettsiden din, så lager vi et konkret forslag til annonser og landingsside for bedriften din, slik det ville sett ut med din merkevare. Du får det på e-post innen et par dager, og det er ditt uansett.

Gratis · ingen binding · vi deler aldri infoen din
Vil du heller ta praten direkte? Book 30 minutter i kalenderen → · victor@arcads.no