Skip to content

Instantly share code, notes, and snippets.

@jmcd
Last active November 25, 2025 15:36
Show Gist options
  • Select an option

  • Save jmcd/c7b89f58ac92786455be236845084215 to your computer and use it in GitHub Desktop.

Select an option

Save jmcd/c7b89f58ac92786455be236845084215 to your computer and use it in GitHub Desktop.
// This is a port of https://x.com/XorDev/status/1894123951401378051 to C#.
// You'll need to install Raylib-cs NuGet package (C# bindings round Raylib) as the thing that allows pixels to happen.
// dotnet add package Raylib-cs
using System.Numerics;
using Raylib_cs;
namespace NotShaders;
internal abstract class Program
{
private static void Main(string[] args)
{
(int width, int height) screenSize = (800, 480);
const int scale = 2;
(int width, int height) textureSize = (screenSize.width / scale, screenSize.height / scale);
Raylib.InitWindow(screenSize.width, screenSize.height, "Not a shader");
Raylib.SetTargetFPS(144);
var screenBuffer = new byte[textureSize.width * textureSize.height * 4];
Texture2D screenTexture;
{
var blankImage = Raylib.GenImageColor(textureSize.width, textureSize.height, Color.Black);
screenTexture = Raylib.LoadTextureFromImage(blankImage);
Raylib.UnloadImage(blankImage);
}
Vector2 resV = new(textureSize.width, textureSize.height);
while (!Raylib.WindowShouldClose())
{
var time = (float)Raylib.GetTime();
Parallel.For(0, textureSize.height, y =>
{
for (var x = 0; x < textureSize.width; ++x)
{
var pixelCoord = new Vector2(x, y);
var pixelColor = ComputeShader(x, y, resV, time);
var index = (y * textureSize.width + x) * 4;
screenBuffer[index + 0] = pixelColor.R;
screenBuffer[index + 1] = pixelColor.G;
screenBuffer[index + 2] = pixelColor.B;
screenBuffer[index + 3] = pixelColor.A;
}
});
unsafe
{
fixed (byte* data = screenBuffer)
{
Raylib.UpdateTexture(screenTexture, data);
}
}
Raylib.BeginDrawing();
Raylib.DrawTextureEx(screenTexture, Vector2.Zero, 0, scale, Color.White);
Raylib.DrawFPS(10, 10);
Raylib.EndDrawing();
}
Raylib.UnloadTexture(screenTexture);
Raylib.CloseWindow();
}
private static Color ComputeShader(int x, int y, Vector2 resolution, float time)
{
// Lower number, more like a big blob
const int iterLim = 8;
// Normalize xy to about [-1 to 1] range, centered on screen.
Vector2 uv;
{
var xy = new Vector2(x, y);
uv = (xy * 2f - resolution) / resolution.Y;
}
// Calculate length scale factor
Vector2 lenScale;
{
var dotP = Vector2.Dot(uv, uv);
lenScale = new Vector2(4f - 4f * MathF.Abs(0.7f - dotP));
}
var v = uv * lenScale;
var cumulativeColor = Vector4.Zero; // 'o' in original
var i = Vector2.Zero;
for (var k = 0; k < iterLim; k++)
{
i.Y += 1f;
// Thank you, Claude, for explaining this bit!
// Update position (Warping)
// Corresponds to: v += cos(v.yx() * i.Y + i + t) / i.Y + .7;
var angleInput = v.yx() * i.Y + i + time;
v += Vector2.Cos(angleInput) / i.Y + 0.7f;
// Accumulate Color
// Corresponds to: o += (sin(v.xyyx()) + 1f) * abs(v.x - v.y)
// This adds layers of color based on the warped position.
var colorContribution = Vector4.Sin(v.xyyx()) + 1f;
var intensity = float.Abs(v.X - v.Y);
cumulativeColor += colorContribution * intensity;
}
var paletteVector = new Vector4(-1, 1, 2, 0);
var exponentPart = lenScale.Y - 4f - uv.Y * paletteVector;
cumulativeColor = MathV4.Tanh(5f * Vector4.Exp(exponentPart) / cumulativeColor);
return new Color(cumulativeColor.X, cumulativeColor.Y, cumulativeColor.Z);
}
}
public static class MathV2
{
extension(Vector2 v)
{
public Vector4 xyyx()
{
return new Vector4(v.X, v.Y, v.Y, v.X);
}
public Vector2 yx()
{
return new Vector2(v.Y, v.X);
}
public static Vector2 operator +(Vector2 a, float s)
{
return new Vector2(a.X + s, a.Y + s);
}
}
}
public static class MathV4
{
public static Vector4 Tanh(Vector4 v)
{
return new Vector4(MathF.Tanh(v.X), MathF.Tanh(v.Y), MathF.Tanh(v.Z), MathF.Tanh(v.W));
}
extension(Vector4)
{
public static Vector4 operator +(Vector4 v, float s)
{
return new Vector4(v.X + s, v.Y + s, v.Z + s, v.W + s);
}
public static Vector4 operator *(float s, Vector4 v)
{
return new Vector4(s * v.X, s * v.Y, s * v.Z, s * v.W);
}
public static Vector4 operator -(float s, Vector4 a)
{
return new Vector4(s - a.X, s - a.Y, s - a.Z, s - a.W);
}
}
}
@jmcd
Copy link
Author

jmcd commented Nov 25, 2025

_noshader

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment