# How YTLite Speed Control Works: A Deep Dive into YouTube's Playback Speed Tweaks

> Discover how YTLite's advanced speed control bypasses native UI for precise YouTube playback speed adjustments with custom gestures and intelligent default rate selection.

- Repository: [Dan/YTLite](https://github.com/dayanch96/YTLite)
- Tags: deep-dive
- Published: 2026-04-22

---

**YTLite combines a custom hold-to-speed gesture with automatic default rate selection to give users fine-grained control over YouTube playback speed without relying on the native UI.**

YTLite is an open-source iOS tweak that modifies the YouTube app to unlock advanced playback features. Understanding how YTLite speed control works requires examining its dual-mode architecture: a temporary speed boost triggered by a long-press gesture, and a persistent default rate applied automatically when videos load. Both features are implemented through Objective-C runtime hooks in the tweak's main source file.

## The Two Speed Control Modes in YTLite

YTLite provides complementary approaches to speed management. Users can choose one or both depending on their viewing habits.

### Hold-to-Speed: Temporary Rate Changes via Gesture

The **hold-to-speed** feature allows temporary speed changes while a finger remains pressed on the video player. This is implemented through a custom `UILongPressGestureRecognizer` injected into the player overlay.

**Configuration and storage:** Users select their preferred speed in **Settings → Hold to speed**. The choice is stored as `speedIndex` in `YTLUserDefaults` with a default value of `1` (Disabled). The available speeds are defined in a hardcoded array:

```objc
@[@0, @2.0, @0.25, @0.5, @0.75, @1.0, @1.25, @1.5, @1.75, @2.0, @3.0, @4.0, @5.0]

```

Index `0` serves as a sentinel for "Disabled," while index `1` (`2.0`) satisfies the native speed-master UI without being user-selectable.

**Runtime implementation:** In `YTLite.x`, the tweak hooks `YTMainAppVideoPlayerOverlayView` to inject the gesture recognizer:

```objc
%hook YTMainAppVideoPlayerOverlayView
- (void)setSeekAnywherePanGestureRecognizer:(id)arg1 {
    if (ytlInt(@"speedIndex") == 0) return %orig;   // Disabled: skip injection
    UILongPressGestureRecognizer *longPress =
        [[UILongPressGestureRecognizer alloc] initWithTarget:self
                                                      action:@selector(speedmasterYtLite:)];
    longPress.minimumPressDuration = 0.3;
    [self addGestureRecognizer:longPress];
}
%new
- (void)speedmasterYtLite:(UILongPressGestureRecognizer *)gesture {
    YTInlinePlayerScrubUserEducationView *edu = self.scrubUserEducationView;
    manageSpeedmasterYTLite(gesture, self.delegate, edu);
}
%end

```

The helper function `manageSpeedmasterYTLite` handles state transitions:

```objc
static void manageSpeedmasterYTLite(UILongPressGestureRecognizer *gesture,
                                   YTMainAppVideoPlayerOverlayViewController *delegate,
                                   YTInlinePlayerScrubUserEducationView *edu) {
    NSArray *speedLabels = @[@0, @2.0, @0.25, @0.5, @0.75, @1.0,
                            @1.25, @1.5, @1.75, @2.0, @3.0, @4.0, @5.0];
    if (gesture.state == UIGestureRecognizerStateBegan) {
        rateBeforeSpeedmaster = delegate.currentPlaybackRate;
        [delegate setPlaybackRate:[speedLabels[ytlInt(@"speedIndex")] floatValue]];
        [edu setVisible:YES];
    } else if (gesture.state == UIGestureRecognizerStateEnded) {
        [delegate setPlaybackRate:rateBeforeSpeedmaster];
        [edu setVisible:NO];
    }
}

```

The native `YTSpeedmasterController` is also hooked to redirect its long-press behavior to the same helper, ensuring consistency when the native speed-master UI is present.

### Default Playback Rate: Automatic Speed on Video Load

The **default playback rate** feature automatically applies a user-selected speed whenever a new video begins playback. This eliminates the need to manually adjust speed for every video.

**Configuration and storage:** The setting is stored as `autoSpeedIndex` in `YTLUserDefaults` with a default of `3` (representing 1.0×). The speed array for this feature omits the sentinel values:

```objc
@[@0.25, @0.5, @0.75, @1.0, @1.25, @1.5, @1.75, @2.0, @3.0, @4.0, @5.0]

```

**Runtime implementation:** The feature hooks `YTPlayerViewController` to inject the speed change after video load:

```objc
%hook YTPlayerViewController
- (void)loadWithPlayerTransition:(id)arg1 playbackConfig:(id)arg2 {
    %orig;
    [self performSelector:@selector(setAutoSpeed) withObject:nil afterDelay:0.5];
}
%new
- (void)setAutoSpeed {
    NSArray *speedLabels = @[@0.25, @0.5, @0.75, @1.0, @1.25,
                            @1.5, @1.75, @2.0, @3.0, @4.0, @5.0];
    YTMainAppVideoPlayerOverlayViewController *ov =
        (YTMainAppVideoPlayerOverlayViewController *)self.activeVideoPlayerOverlay;
    [ov setPlaybackRate:[speedLabels[ytlInt(@"autoSpeedIndex")] floatValue]];
}
%end

```

The 0.5-second delay ensures the player overlay is fully initialized before the speed change is applied.

## Key Implementation Files in the YTLite Repository

| File | Purpose | Key Components |
|------|---------|--------------|
| `YTLite.x` | Core speed control implementation | `speedmasterYtLite:`, `setAutoSpeed`, `manageSpeedmasterYTLite` |
| `Settings.x` | User interface for speed selection | Speed picker UI, `ytlSetInt` calls for `speedIndex` and `autoSpeedIndex` |
| `Utils/YTLUserDefaults.m` | Default configuration values | Registration of `speedIndex` and `autoSpeedIndex` with default values |

The extra speed options (2.5×–5×) are implemented in a separate hook of `YTVarispeedSwitchController`, extending the native speed selection UI when the corresponding toggle is enabled.

## Summary

- **YTLite speed control** operates through two independent but complementary mechanisms: a **hold-to-speed gesture** for temporary changes and a **default playback rate** applied automatically on video load.

- Both features rely on **runtime injection** into YouTube's native player classes (`YTMainAppVideoPlayerOverlayView`, `YTPlayerViewController`) using the Logos hooking framework.

- Configuration persists through a **custom `YTLUserDefaults` wrapper**, with `speedIndex` controlling the gesture and `autoSpeedIndex` controlling the startup speed.

- The implementation reuses a **centralized helper function** (`manageSpeedmasterYTLite`) to keep gesture handling consistent across native and injected interfaces.

## Frequently Asked Questions

### What speeds are available in YTLite's speed control?

YTLite supports playback rates from **0.25× to 5.0×**, including granular options like 0.75×, 1.25×, and 1.75× that are often missing from native YouTube controls. The hold-to-speed feature additionally includes a special 2.0× entry at index 1 to satisfy the native speed-master UI's internal validation.

### How does YTLite's hold-to-speed gesture differ from YouTube's native speed controls?

The **hold-to-speed gesture** in YTLite is a **temporary** speed change that only applies while the user maintains pressure on the screen. YouTube's native speed controls require navigating through menus and apply a **persistent** change until manually adjusted. YTLite's implementation also allows higher maximum speeds (up to 5×) than the native app typically permits.

### Does YTLite remember the last speed I used?

YTLite provides **two independent memory mechanisms**: the hold-to-speed feature remembers your chosen speed index for the gesture, while the default playback rate feature remembers a separate speed that's applied automatically when each video loads. Neither automatically adapts based on your last-used speed in a session; both require explicit configuration through the Settings interface.

### Where is the speed control configuration stored in YTLite?

Speed preferences are stored in **NSUserDefaults** through the `YTLUserDefaults` wrapper class in `Utils/YTLUserDefaults.m`. The keys are `speedIndex` (for hold-to-speed, default 1) and `autoSpeedIndex` (for default playback rate, default 3). These values persist across app launches and are registered with defaults on first initialization.