srtDisplay

Secure · Local-only

DJI drone telemetry · .SRT flight log

Read your flight, frame by frame.

Drop a DJI .SRT log and see the whole flight — interactive map, altitude & speed charts, and timeline playback. Nothing is uploaded — it all runs in your browser.

Drop a .SRT here, or

No account · no upload · works offline

What it does

01

100% private

Your .SRT is parsed on your own device. No file ever leaves the browser — no server, no account.

02

Map & charts

Colour-coded flight path on a satellite map, plus altitude, ground speed, distance & vertical-speed plots.

03

Shareable links

One click packs the whole flight into a URL — send it and the recipient sees the exact same view.

04

Timeline playback

Scrub or play the flight back in real time; the map cursor and every chart stay in sync.

Reference

Make a clickable flight link

srtDisplay opens a flight three ways. Method 1 needs no code. Methods 2 and 3 let you generate links programmatically — the code below mirrors exactly what the app does internally.

1

Drop & copy

No code
  1. Drop your .SRT onto the box above (or click Try a sample flight).
  2. Once the flight renders, click 📋 Copy shareable link.
  3. Paste it anywhere. The flight is packed into the URL after #d= — so the recipient's browser rebuilds it locally, and nothing is uploaded either.

Very long flights are downsampled to fit. If a flight is too big to embed, host the raw .SRT and use Method 2 instead.

2

Link to a hosted .SRT with ?url=

Host the raw .SRT somewhere reachable and pass it as a query parameter. The file is fetched by the visitor's browser, so the host must send an Access-Control-Allow-Origin header (be CORS-open) for cross-origin files:

URL hosted-file link
https://srtdisplay.techmagic.info/?url=https://example.com/flights/DJI_0007.SRT

Here's a live one that loads this site's own sample (same-origin, so no CORS needed):

Open the sample flight via ?url=
3

Embed the flight in the link with #d=

The self-contained links use a compact v1 payload: a columnar JSON document → gzipbase64url (URL-safe, padding stripped), appended after #d=. Because it lives in the URL fragment, the payload never even reaches a server.

v
format version — always 1
meta
{ name, start, camera } — flight label, start time, camera model
cols
["t","lat","lon","rel","abs","iso","sh","fn"] — column order for every row
rows
array of rows: [t, lat, lon, rel, abs, iso, sh, fn]
per row
t = seconds (2dp) · lat/lon = degrees (6dp) · rel/abs = metres (2dp) · iso = number|null · sh = shutter string|null · fn = f-number|null
JSON v1 payload (before gzip)
{
  "v": 1,
  "meta": { "name": "Sample flight", "start": "2026-07-22 15:11:35", "camera": "" },
  "cols": ["t","lat","lon","rel","abs","iso","sh","fn"],
  "rows": [
    [0, 35.8971, 14.5119, 0, 79.12, 400, "1/400.0", 2.2],
    [1.03, 35.8972, 14.512, 12.4, 91.5, 400, "1/400.0", 2.2]
  ]
}
JavaScript build a #d= link in the browser
// Columns, in the exact order the payload expects.
const COLS = ["t","lat","lon","rel","abs","iso","sh","fn"];

// Round the way srtDisplay does: coords 6dp, everything else 2dp.
const r6 = v => v == null ? null : Math.round(v * 1e6) / 1e6;
const r2 = v => v == null ? null : Math.round(v * 100) / 100;

function toColumnar(flight) {
  return {
    v: 1,
    meta: { name: flight.meta.name || "", start: flight.meta.start || "", camera: flight.meta.camera || "" },
    cols: COLS,
    rows: flight.points.map(p => [
      r2(p.t), r6(p.lat), r6(p.lon), r2(p.rel), r2(p.abs),
      p.iso ?? null, p.sh ?? null, p.fn ?? null
    ]),
  };
}

// gzip via the built-in CompressionStream, then URL-safe base64 (no padding).
async function gzip(str) {
  const cs = new CompressionStream("gzip");
  const buf = await new Response(new Blob([str]).stream().pipeThrough(cs)).arrayBuffer();
  return new Uint8Array(buf);
}
function b64url(u8) {
  let s = "";
  for (const b of u8) s += String.fromCharCode(b);
  return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
}

async function makeShareLink(flight) {
  const json = JSON.stringify(toColumnar(flight));   // compact, no spaces
  const payload = b64url(await gzip(json));
  return `https://srtdisplay.techmagic.info/#d=${payload}`;
}
Python build a #d= link from parsed points
import json, gzip, base64

COLS = ["t", "lat", "lon", "rel", "abs", "iso", "sh", "fn"]

def rnd(v, nd):
    return None if v is None else round(v, nd)

def to_columnar(points, name="", start="", camera=""):
    return {
        "v": 1,
        "meta": {"name": name, "start": start, "camera": camera},
        "cols": COLS,
        "rows": [
            [rnd(p["t"], 2), rnd(p["lat"], 6), rnd(p["lon"], 6),
             rnd(p["rel"], 2), rnd(p["abs"], 2),
             p.get("iso"), p.get("sh"), p.get("fn")]
            for p in points
        ],
    }

def make_share_link(points, **meta):
    doc = to_columnar(points, **meta)
    raw = json.dumps(doc, separators=(",", ":")).encode("utf-8")   # compact JSON
    payload = base64.urlsafe_b64encode(gzip.compress(raw)).rstrip(b"=").decode()
    return f"https://srtdisplay.techmagic.info/#d={payload}"

# points: list of dicts with t, lat, lon, rel, abs (+ optional iso, sh, fn)
# print(make_share_link(points, name="My flight"))