Skip to content
HY Devlog
Go back

Three Forks Later, an Official Version Appeared

Looking for how to add a glitch effect to a game project as a piece of staging, I saved the README of saimarei/URPGlitch, a glitch effect package for URP. Three installation methods plus the renderer feature and volume setup order — a document you can follow straight through.

Open that repository today and there’s a notice at the top of the README.

I have moved on to using the Godot engine and will no longer be updating this repository.

It directs you to keijiro’s KinoGlitchURP if problems come up. That sentence wasn’t there when I saved it. Checking it out, the entire lineage this package stands on is in that state — and an official version had appeared at the end of it.

Table of contents

Table of contents

A lineage of three forks

The README names its sources: based on mao-test-h’s project, which was in turn inspired by keijiro’s KinoGlitch. Opening each one:

RepositoryTargetLicenseCurrent state
keijiro/KinoGlitchlegacy pipelineMITthe original
mao-test-h/URPGlitchUnity 2021.3+ / URP 12.1.7MIT“not actively maintained”
saimarei/URPGlitchUnity 6000.0MIT“will no longer be updating”
keijiro/KinoGlitchURPUnity 6000.0+ / URPUnlicenseofficial URP version

Both of the middle two state that maintenance has stopped. mao-test-h’s README says:

Please note that this project was implemented for study purposes and is not actively maintained.

That’s why the forks chained. The original targeted the legacy pipeline, somebody ported it to URP as a study, and this clipping’s package reworked that for Unity 6. And in the meantime the original author shipped a URP version himself. That’s where saimarei/URPGlitch now points.

Where and why you’d use it

The package README covers installation and setup only. When you’d reach for a glitch effect isn’t in there, so here it is.

When you’d use it

What these share is that the intensity has to change over time. It isn’t an effect you set once in the Inspector; mostly you shake it and let it settle from code.

Driving it from code

The following targets KinoGlitchURP, the official version. You grab the controller component attached to the camera directly.

using System.Collections;
using KinoGlitch;
using UnityEngine;

[RequireComponent(typeof(Camera))]
public class GlitchFeedback : MonoBehaviour
{
    private const float MIN_INTENSITY = 0f;

    [Header("Damage Flash")]
    [SerializeField, Range(0f, 1f), Tooltip("Peak intensity at the moment of the hit")]
    private float _peakIntensity = 0.6f;

    [SerializeField, Range(0.05f, 1f), Tooltip("Seconds taken to settle back down")]
    private float _falloffSeconds = 0.35f;

    private DigitalGlitchController _digital;
    private Coroutine _running;

    private void Awake()
    {
        if (!TryGetComponent(out _digital))
        {
            _digital = gameObject.AddComponent<DigitalGlitchController>();
        }

        _digital.Intensity = MIN_INTENSITY;
    }

    public void PlayDamageFlash()
    {
        if (_running != null)
        {
            StopCoroutine(_running);
        }

        _running = StartCoroutine(FlashRoutine());
    }

    private IEnumerator FlashRoutine()
    {
        _digital.Intensity = _peakIntensity;

        float elapsed = 0f;
        while (elapsed < _falloffSeconds)
        {
            elapsed += Time.deltaTime;
            float t = elapsed / _falloffSeconds;
            _digital.Intensity = Mathf.Lerp(_peakIntensity, MIN_INTENSITY, t);
            yield return null;
        }

        _digital.Intensity = MIN_INTENSITY;
        _running = null;
    }
}

The analog side has five parameters, so the combination decides its character. For a constant layer that grows at low health:

// Take a health ratio (0-1); the lower it goes, the more the screen breaks up
public void ApplyHealthDistortion(float healthRatio)
{
    float severity = Mathf.Clamp01(1f - healthRatio);

    _analog.ScanLineJitter    = severity * 0.35f;  // scan lines slip
    _analog.VerticalJump      = severity * 0.10f;  // the image hops vertically
    _analog.HorizontalShake   = severity * 0.15f;  // horizontal judder
    _analog.ColorDrift        = severity * 0.30f;  // colors separate
    _analog.HorizontalRipple  = severity * 0.20f;  // horizontal rippling
}

Raising all five at the same rate is usually too much. ColorDrift and ScanLineJitter carry most of the “the screen is broken” impression, while VerticalJump gets tiring on the eyes at surprisingly small values. Giving each axis its own ceiling, as above, works better.

What to watch for on performance

The KinoGlitchURP README has this line:

Digital Glitch still runs at zero Intensity because it must keep updating its internal frame history. Disable the component if you want to eliminate its cost entirely.

Dropping intensity to zero leaves the cost. It has to keep holding the previous frame. So while the coroutine above ends at zero, if you won’t be using it for a while, turn the component off.

// Nothing coming up for a while — switch the component off
_digital.enabled = false;

The analog side behaves differently. The README says “Analog Glitch skips its pass when all properties are set to zero.” All zero and the pass is skipped. Just know that zero means different things for the two of them.

Switching changes how you drive it

Moving to the official version isn’t only a package swap. The structure for applying the effect is different.

The clipping’s packageKinoGlitchURP
ControlGlobal Volume + volume overridecontroller component on the camera
Component namesAnalog/Digital Glitch VolumeAnalogGlitchController, DigitalGlitchController
Default injection pointAfter Rendering Transparents (set manually)after post processing (change via Pass Event)
InstallationGit URLscoped registry

The clipping’s package rides URP’s volume system. You make a Global Volume in the scene and add an override to its profile, which suits per-area values and blending.

The official one attaches a component to the camera. That’s why the example code above can grab it straight with TryGetComponent. Skipping the volume makes it simpler to handle from code, but whatever you got from volume blending you now build yourself.

Less about which is better than about your existing code not carrying over. Anything that dug through a volume profile with TryGet has to be rewritten.

The licenses diverge

Only the last row of that lineage table differs. The original KinoGlitch and its forks are MIT; the official URP version KinoGlitchURP is Unlicense.

The clipping’s README states its license this way:

This project is released under the same license as keijiro/KinoGlitch.

A description you have to go to the original to resolve — and checking, it is MIT, which matches what GitHub labels the repository. Nothing contradicts. But writing a license as “go look at that other repository” means you won’t know if that repository later changes it. Which is effectively what happened: the same author’s new repository shipped under a different one.

For a commercial project, which one you use changes your attribution obligations. MIT requires shipping the copyright notice and license text; Unlicense is close to public domain and carries no such duty.

The installation methods differ too

The clipping’s route is a Git URL. In Package Manager, + → Install package from Git URL, or edit manifest.json directly.

{
  "dependencies": {
    "com.subbu.urp-glitch": "https://github.com/saimarei/URPGlitch.git",
    "com.unity.collab-proxy": "2.5.2"
  }
}

The official one uses a scoped registry.

Install the KinoGlitch URP package (jp.keijiro.kino-glitch.universal) from the ‘Keijiro’ scoped registry in Package Manager.

The difference shows up in practice. A Git URL doesn’t pin a version. That line points at the repository’s default branch, so if the repository changes, restoring packages later brings different code. If the repository disappears, restoring fails outright. Holding a repository that has declared maintenance over via a Git URL is a risk in itself.

You can pin with a tag (.git#tag), but the scoped registry is steadier for version management.

What’s still in the clipping

The step where you assign the shader by hand. From the setup instructions:

Set the Shader field by selecting an appropriate shader (click the eye icon to reveal hidden shaders).

You have to wire a shader into the renderer feature yourself, picking from hidden shaders revealed by the eye icon. That’s a shape where shaders go missing from builds, because a shader no scene material references is a stripping candidate. When it works in the editor and not in a build, this is the first place to look.

VR was reverted. The repository’s commit list contains Revert "made texture usage to be compatible with vr". An attempt at VR compatibility existed and was rolled back. If you were planning to use it in XR, that one line is your answer.

Old versions live in Releases. From the top of the README:

If you are coming from the older URP Glitch video, you need to download the older 2021.3.8f1+ package from the Releases section.

On a pre-Unity-6 project you take it from Releases, not the Git URL. Pasting the Git URL on sight is where the compile breaks come from.

Wrapping up

Saving a repository README is worth doing — install commands and setup order in one place. But a README reflects the repository’s present state, and saving it severs that relationship. In this case, in the interval, the author closed his door and the original author opened one.

So what to check first in a package clipping is settled: not the install command, but the top of the README and the date of the last commit. Either one alone decides whether to read what’s below it.

There’s something wry about starting out to add one piece of staging and ending at a choice of package. Then again, a glitch is an effect whose intensity you keep shaking to match events, so you’re in its API constantly — which makes whether that API stays put an actual problem. It turned out to be the kind of thing where who is maintaining it weighs as much as how it looks on screen.

References


Share this post:

Previous Post
What FreezePositionY Freezes Is World Y
Next Post
One Read Is Not One Message