GDTwitch Integration Toolkit
Godot 4.7 Library for Twitch Integration
Lots of features in this project to make twitch integration easy. Check out the provided examples/ to get started quickly.
Twitch Integration
- Supports all versioned EventSub events.
- Device Code Grant Flow for security-standard OAuth logins with the
GDTwitchDeviceCodeFlowWidget. - Supports sending chat messages, replies, and pinned messages.
- Supports EventSub reconnect requests, and automatic reconnecting on dropped connections (using backoff).
- Use signals for EventSub events with
GDTwitchEventSub - Use coroutines to load animated emotes from the web and cache with
GDTwitchImages.
Twitch Images Support:
- Emotes Support:
- User badges
- User avatars
Render chat messages
![]() |
|---|
- Use
GDTwitchEmoteChatLabelto render a chat message with embedded animated zero-width emotes. - Use
GDTwitchChatRowto render an entire chat row with avatar, badges, colorized usernames, and the chat message with embedded animated zero-width emotes.
Examples
Check out the examples/ directory for the most reliable source of documentation.
They should provide ideas on how to get started.
![]() |
|---|
examples/main_minimal_example.gd- This example demonstrates how to create a
GDTwitchEventSubconnection with just a few lines of code. - This example listens to chat messages, and sends a message to chat.
- Run with
$ godot --headless -s res://examples/main_minimal_example.gd
- This example demonstrates how to create a
examples/main_device_code_example.gd- This example demonstrates the use of the
GDTwitchDeviceCodeFlowWidget, which allows your app to complete the Device Code Grant Flow. - It also demonstrates the refresh-token ability of the Widget.
- Run with
$ godot -s res://examples/main_device_code_example.gd
- This example demonstrates the use of the
examples/games_example- This example includes a couple programs to choose from:
chat: Demonstrates use of theGDTwitchChatRowwhich renders avatars, badges, and chat messages.plinko: Demonstrates reading from chat to control a game.
- This example features a UI for inputting the channels you want to interact with.
- This example also demonstrates saving the
AppTokento disk for quick restarts. - Run with
$ godot .as it is the primary binary.
- This example includes a couple programs to choose from:
Usage
GDTwitchEventSub
GDTwitchEventSub is your primary class for engaging with the Twitch EventSub API. Every possible message has a type-safe concrete type defined under GDTwitchTypes.
Using GDTwitchEventSub requires the following:
- Create a cancel token (
GDTwitchAsync.Context) - Events to subscribe to (
GDTwitchEventSub.EventSelection) - Your connection parameters (
GDTwitchEventSub.StartArgs)var context := GDTwitchAsync.Context.ongoing() var events := GDTwitchEventSub.EventSelection.all() # client_secret is required only if you want # badges, avatars, or to send messages. # Otherwise, provide channel_id and user_id. var start_args := GDTwitchEventSub.StartArgs.new() </span> .with_username(username) </span> .with_channel(channel) </span> .with_client_id(client_id) </span> .with_client_secret(client_secret) </span> .with_access_token(oauth_token) </span> .with_event_selection(events)
Then start the GDTwitchEventSub!
var events : GDTwitchEventSub
func _ready() -> void:
events.on_any_event.connect(_on_any_event) # Use a more specific signal!
events.start(context, start_args)
and you're off and running.
To close events:
context.cancel()
and to cleanup any resources at app shutdown, or when a child context is no longer needed:
context.join() # or context.cancel_and_join() if this hasn't been cancelled.
#### GDTwitchImages
GDTwitchImages is your one-stop shop for image downloads, caching, and decoding. All images and animations are retrieved as Texture so you are free to use them how you see fit.
To use GDTwitchImages, you only have to create an instance:
var images := GDTwitchImages.new()
These instance are cheap. However, the image download deduplication only happens per-instance, so if you have many overlays you may want to share an instance instead of creating multiple new ones to prevent downloading the same emotes, badges, or avatars concurrently.
# Resolve all emotes (all emote_spans.resolved_emotes will be updated)
await images.resolve_emotes(context, event.message.emote_spans)
# Resolve any _avatar_url field:
var avatar_image := await images.resolve_avatar_url(context, message.chatter_avatar_url)
# Resolve all badges (all badges.resolved_badges will be updated)
await images.resolve_badges(context, message.badges)
NOTE: When an EmoteSpan or ChatMessageBadge has already been resolved, it holds a reference to resolved image. However, resolving the same emote later on a new instance will just use the Texture in memory (if its weak reference is alive) or check the cache on disk.
Often you want to download multiple images concurrently:
# Resolve all images and/or add them to the cache.
await GDTwitchAsync.await_all(
images.resolve_emotes.bind(context, event.message.emote_spans),
images.resolve_badges.bind(context, event.badges),
images.resolve_avatar_url.bind(context, event.chatter_avatar_url),
)
context is a cancel token with some interesting properties. See the section on GDTwitchAsync.Context for tips and tricks.
GDTwitchAsync.Context
GDTwitchAsync.Context is a versitile utility used extensively in the GDTwitch internals. However, it provides some great benefits as a public-facing API as well, such as timeouts and cancellation.
It can be constructed in at least these following ways:
# Context that continues until it is cancelled.
var context := GDTwitchAsync.Context.ongoing()
# Context that continues until it cancelled or the time expires.
# Useful for image downloads that you may want to limit their time.
var image_context := GDTwitchAsync.Context.new_timeout(15.0)
# Create a context where if the parent is cancelled, the child is cancelled, too.
var child_context := context.child()
var download_context := context.child_with_timeout(15.0)
GDTwitchEventSub expects the context passed into start to be cancelled at some point so that the serve can shutdown and begin cleanup.
You application should call context.cancel() when the GDTwitchEventSub is no longer needed, such as when the managing Node is removed from the tree (_exit_tree()).
Additionally, when the application shuts down, or a child context is no longer needed, you should also call context.cancel_and_join() or context.join() to free any remaining resources (threads and workers) for a clean exit.
NOTE: context.cancel() will cancel any children GDTwitchAsync.Context.
NOTE: context.join() will join any children GDTwitchAsync.Context.
NOTE: context.is_done() is the way to check if a context has been cancelled and work should be stopped.
GDTwitchDeviceCodeFlowWidget
![]() |
|---|
GDTwitchDeviceCodeFlowWidget is a widget that initiates the Device Code Grant Flow for your users. If offers provides signal on_app_token(app_token: GDTwitchEventSub.AppToken, error: String) which can be used to get the GDTwitchEventSub.AppToken required for events.start(...)
See this widget in action with main_device_code_example.gd.
This widget makes use of the underlying GDTwitchDeviceCodeFlow utility, which can be used to create your own custom widget. The GDTwitchDeviceCodeFlow is heavily commented, so check it out for more details.
This widget also provides the ability to refresh a token while displaying the token status to the user. The example main_device_code_example.gd demonstrates how to do this in 3 easy steps.
This namespace-class holds all of the concrete types for all of the event you can subscribe to.
There are some artificial fields in some of the various types. Namely, _avatar_url fields are not part of the Twitch EventSub API, but are made available so that those images can be downloaded.
There is plenty more, but these are the basics and then some. When exploring the codebase, the general guideline is that the more underscores you see, the more internal the API. Have fun.
Project Notes
GDTwitch libraryThe architecture of the project allows you to integrate the gd-twitch library without the use of specialized nodes. This encourages a flexibility in implementation in your own projects. To make this possible, the codebase makes use of many components that perform tasks, such as network IO, threading, and image request deduplication using threads in thread-safe patterns that are ultimately exposed to your project as coroutines or signals.
- The test directory includes a GDScript code coverage report tool based on godot-code-coverage, updated for Godot 4.7. See
test/coverage.gdfor details. - Injected fakes for Websocket, Http, and GDTwitchClock to override network I/O and time.
- A utility for saving event samples to disk makes it easy to replicate real-world behavior while tests are offline.
This project will use Semantic Versioning after 1.0. See https://semver.org/ for details.
In summary, given a version number MAJOR.MINOR.PATCH, increment the:
- MAJOR version when you make incompatible API changes
- MINOR version when you add functionality in a backward compatible manner
- PATCH version when you make backward compatible bug fixes
Pre 1.0 releases will bump the MINOR an PATCH versions depending on the size and scope of changes.
Until 1.0, backwards compatibility is not guaranteed.
AI Notice
- All code is reviewed by humans but some parts of this project were implemented by AI and were reviewed and verified by a human.
- Many hours of human attention and care have gone into ensuring the code is high quality and reliable.
- Notable generated content:
- Example peg images are AI generated.
- GDTwitchTypes were AI generated from documentation.
- Many tests were AI generated after the foundation for testing was created.
Changelog for version 0.17.0
No changelog provided for this version.


