skip to content
ON AIR 00:00:00:00 group screen sharing · linux + windows v3.10.2

Share your screen with your friends and watch theirs with genuinely low latency. Anyone can go live, everyone can watch, talk and chat. No accounts and no ads, and on Windows there's nothing to install.

v3.10.2 · every version and file · open source (MIT)
17ms
end to end, on a local network
20%
of one core for 1080p60 HEVC
0
accounts, ads or trackers
connect.zig — telinha
  • telinha
  • src
  • capture_linux.c
  • connect.zig
  • server.zig
  • theme.zig
  • ui.zig
  • bots
  • README.md
264pub fn race(targets: []const Target, version: u16, timeout_ms: u32, cancel: *const std.atomic.Value(bool), fail: *Failure) Error!Winner {265  if (targets.len == 0) return error.UnknownHost;266  const n = @min(targets.len, max_targets);267  const r = gpa.create(Race) catch return error.ThreadFailed;268  r.* = .{ .refs = .init(1), .total = n, .targets = undefined };269  @memcpy(r.targets[0..n], targets[0..n]);270  for (0..n) |i| {271    _ = r.refs.fetchAdd(1, .monotonic);272    sync.spawn(attempt, .{ r, i, @as(u32, @intCast(i)) * 250, version }) catch {273      _ = r.refs.fetchSub(1, .monotonic);274      r.mutex.lock();275      r.finished += 1;276      r.mutex.unlock();277    };278  }279  defer r.release();280  const t0 = sync.nowMs();281  r.mutex.lock();282  defer r.mutex.unlock();283  while (r.winner == null and r.finished < r.total) {284    if (cancel.load(.acquire) or sync.nowMs() - t0 > timeout_ms) {285      r.cancel.store(true, .release);286      fail.err = if (cancel.load(.acquire)) error.Cancelled else error.ConnectFailed;287      return fail.err;288    }289    r.cond.timedWait(&r.mutex, 50);290  }291  if (r.winner) |w| return w;292  fail.err = if (r.version_mismatch) |v| blk: {293    fail.server_version = v;294    break :blk error.VersionMismatch;295  } else if (r.not_telinha) error.NotTelinha else if (r.refused) error.ConnectionRefused else if (r.unknown and r.total == 1) error.UnknownHost else error.ConnectFailed;296  return fail.err;297}
mainZig 0.16UTF-8LF
telinha1920×1080 · 60
~/telinha — zsh

    
in #geral · 4
AAnaLIVE
BBruno
CCarol
MMusicBOT
NOW PLAYING Soma FM — Groove Salad requested by Carol
⏸⏭queue
Message #geral

Simulated in your browser, so nothing here is a real stream. Click a small screen to cut to it.

Live test

Feel the 17 ms

Draw on your screen. Your friend's screen gets it with the delay you choose, so you can feel what a number on a page actually means.

Your screen
draw here
Your friend's screen17 ms
delay
  1. CaptureDMA-BUF or DXGI, straight from the compositor
  2. EncodeNVENC, 8 ms
  3. SendUDP, resending only what was lost
  4. DecodeNVDEC, 6 ms
  5. Showright into an OpenGL texture

Measured on an i5-2400S from 2011 with a GTX 1050 Ti, at 1080p60 in HEVC, on a local network. Over the internet, add the distance between you. In this test, your browser and monitor add their own delay on top. How the pipeline works →

Bulletin

What's new

What landed in the latest version.

  • Deafen only silences the call: audio from the streams you're watching keeps playing (deafen used to silence everything, even your friend's video). Sound cues keep playing too.
Rundown

Why telinha

  1. 01

    Fast

    Capture, encoding and decoding stay on the GPU. Latency is measured on every frame, and the stream adapts before a delay builds up.

  2. 02

    One invite, done

    The invite carries every way into the room: LAN, router port, IPv6, public link. Each person joins through the fastest one that works.

  3. 03

    Private

    Every connection is encrypted with X25519 and ChaCha20-Poly1305, with the password in the key derivation. The server's identity is verified.

  4. 04

    Light

    The interface only redraws when something changes. Features you don't use cost nothing: no CPU, no GPU, no bandwidth.

  5. 05

    Portable

    On Windows it's a single .exe. On Linux it runs on Wayland and X11, on any distro with glibc 2.31+.

  6. 06

    Updates itself

    Shows what changes, checks the release signature and swaps its own executable. One click, or on its own.

Connection

One invite, every way in

You send one link. The app races every address inside it, and the first to answer wins.

telinha://192.168.0.10:7777,203.0.113.7:7777,[2804:14c::1]:7777,bore.pub:41234!kzdv…
  • Local network—
  • Router port—
  • IPv6—
  • Public link—

Timing slowed down so you can see it. Whichever way it goes, the conversation is end-to-end encrypted and the invite carries the room's fingerprint, so a tunnel in the middle only sees scrambled bytes. Every way to connect →

Got a link?

Click it. The page opens telinha straight into the room or shows how to get it for your system. The invite stays in your browser.

Got a telinha://?

Copy it and open telinha: it notices the copied invite. Or paste it into Join a room.

On a phone?

There's no phone app. If the room has the watch-in-browser page, ask whoever invited you for its link.

Remote

Change the channel

These are the app's own themes, read straight from its source. Press one, or a key from 0 to 6, and this whole site tunes in, docs included. Channel 00 is just for the site: the colors of the husky's eyes.

Guests

Bots

Music in the call, and your own, in Python.

A bot is one more in the room

It reads and sends messages, answers commands, reacts, plays sound in the call and handles files. It shows up with a BOT tag and never becomes admin; the room owner can refuse bots or require a token.

  • A ready-made music bot: YouTube, SoundCloud and Spotify links, search by name, a folder, radios. !play, !search, !queue, !skip.
  • Cards with buttons: a cover, fields, a progress bar that moves by itself and buttons anyone can click; the bot changes the card in place.
  • A Python SDK: commands with typed arguments, events, private messages, music and files.
  • Runs on a VPS, a Raspberry Pi or your PC. It costs the streamer nothing.
dice.py
import random
from telinha import Bot

bot = Bot("telinha://my.room:7777#password", "Dice")

@bot.command("dado", help="rolls a die")
async def dado(ctx, sides: int = 6):
    await ctx.reply(f"rolled {random.randint(1, sides)}")

bot.run()
The music bot in a room: the !play request and the NOW PLAYING card with the progress bar and buttons
In the app: the bot's card, with the bar that moves by itself and the buttons, and the green ring of whoever is playing.
Archive

Screenshots

The real app, nothing simulated this time.

The room. Who's live on top, the stream in the middle, members and chat on the side.
Going live. What to show, which audio, the content type, resolution, frame rate and quality.
Home screen. Join or create a room, straight into a channel if you like.
Server stats. What the admin sees: CPU, memory, bandwidth and the likely bottleneck.
Global hotkeys. Any key or mouse button, active while a game is focused.
Performance. Automatic quality, cursor outside the video and a test of your PC.
Tune in

Install

Pick your system.

No sudo: downloads the latest release, checks its SHA256, puts telinha in ~/.local/bin, adds it to your app menu and makes telinha:// invites open in the app. Run it again to update.

$ curl -fsSL https://raw.githubusercontent.com/RshaCuDeVidro/telinha/main/install.sh | sh
  1. Create a room. Open telinha and click Create room. The password is optional.
  2. Invite people. Open port on router for a direct connection, or Create public link if your ISP uses CGNAT. Then Copy invite.
  3. Join. Whoever gets it pastes the telinha://… invite on the home screen. If it's already copied, it pastes itself.
  4. Go live. Share screen, pick what to show, the sound and the quality, then Go live. Everything can change mid-stream.
  5. Watch and talk. Click anyone who is live. F11 fullscreen, Ctrl+M microphone, Ctrl+D deafen. Full guide →

End of transmission

Download it, create a room and send the invite. If you get stuck anywhere, the wiki has the way through.