소스 정보
- 저장소
- cwilliams5/Alt-Tabby
- 최근 소스 활동
- 2026년 3월 7일 05:24
- 감지된 SKILL.md 언어
- 영어
- 스타
- 4
- 포크
- 0
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/cwilliams5/Alt-Tabby --skill shader-convert명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
Audit all lint-ignore suppressions for appropriateness and overuse
Pre-flight checklist — run high-signal review skills before a release
Discover functions that block the main thread, then micro-audit each for internal optimization opportunities
SOC 직업 분류 기준
SKILL.md 표시 중
| name | shader-convert |
| description | Convert Shadertoy GLSL shaders to HLSL for Alt-Tabby's D3D11 pipeline |
| user-invocable | true |
| disable-model-invocation | true |
Convert Shadertoy GLSL shaders to the Alt-Tabby HLSL pixel shader format.
/shader-convert — Scan src/shaders/ (and subdirs mouse/, selection/) for any .glsl without matching .hlsl, convert all/shader-convert <Shadertoy URL> — Fetch shader from Shadertoy via Playwright, then convert/shader-convert <pasted GLSL + metadata> — Convert manually pasted shader sourceNot supported: Multi-buffer shaders (Buffer A/B/C/D tabs). Our pipeline is single-pass only. Skip shaders that require inter-frame feedback or multi-pass rendering.
src/shaders/ and subdirs src/shaders/mouse/, src/shaders/selection/ for any name.glsl that has no matching name.hlsl.hlsl (and .json if missing)src/shaders/mouse/, selection shaders in src/shaders/selection/, background shaders in src/shaders/shadertoy.com/view/)Requires the Playwright MCP server. Extracts shader source, metadata, and iChannel textures automatically.
The Playwright MCP is not loaded by default — it's toggled on-demand to avoid context bloat.
Check if you have access to mcp__playwright__browser_navigate. If NOT available:
powershell -File tools/toggle-playwright-mcp.ps1 on
browser_navigate → https://www.shadertoy.com/view/{id}
If Cloudflare challenge appears, wait ~10s for auto-pass. Verify the page title changes from "Shader - Shadertoy BETA" to the shader name.
() => {
const st = window.gShaderToy;
if (!st) return { error: 'gShaderToy not found' };
// Metadata
const info = st.mInfo;
// Code from each pass via CodeMirror Doc
const passes = st.mPass.map((p, i) => {
const code = p.mDocs && typeof p.mDocs.getValue === 'function'
? p.mDocs.getValue() : null;
return { index: i, code, charCount: code ? code.length : 0 };
});
// Tab names → map pass index to role
const tabNames = {};
for (let i = 0; i < 10; i++) {
const tab = document.getElementById('tab' + i);
if (tab) tabNames['tab' + i] = tab.textContent.trim();
}
// Pass types from effect renderer (image, common, buffer, sound, cubemap)
const passTypes = st.mEffect ? st.mEffect.mPasses.map( ({
: i, : p.
})) : [];
iChannels = [];
(st. && st..) {
imgPass = st...( p. === ) || st..[];
(imgPass && imgPass.) {
iChannels = imgPass..( {
(!inp) ;
.(.(inp.));
});
}
}
{
: { : info., : info., : info., : info. },
passes, tabNames, passTypes, iChannels
};
}
Check passTypes — if any pass has type of "buffer", "sound", or "cubemap":
"buffer" → STOP: Multi-buffer shader, not supported. Tell user."sound" → STOP: Audio-output shader, not supported."cubemap" → STOP: Cubemap pass, not supported."image" and "common" are valid.Check iChannels for audio inputs:
mType !== "texture" (e.g., "music", "musicstream", "webcam", "video", "keyboard"), note it. Audio channels will need synthetic beat replacement (see §7). Webcam/video/keyboard → skip the shader.Combine passes into a single .glsl file:
"common" tab exists: put Common code first, then // --- Image --- separator, then Image codename from info.name → snake_case (e.g., "Power (Chainsaw Man)" → power_chain_saw_man)For each non-null iChannel with mType === "texture":
// In browser_run_code (needs Playwright page object for download API)
// NOTE: page.evaluate only accepts one arg — wrap multiple values in an object
async (page) => {
const textures = [
{ url: 'https://www.shadertoy.com' + mSrc0, file: 'name_i0.png' },
{ url: 'https://www.shadertoy.com' + mSrc1, file: 'name_i1.png' }
];
const results = [];
for (const tex of textures) {
const downloadPromise = page.waitForEvent('download', { timeout: 15000 });
await page.evaluate(({url, filename}) => {
return fetch(url).then(r => r.blob()).then(blob => {
const blobUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = blobUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
..(a);
.(blobUrl);
});
}, {: tex., : tex.});
download = downloadPromise;
download.( + tex.);
results.(tex.);
}
results;
}
https://www.shadertoy.com + mInfo.mSrc (e.g., /media/a/...png)src/shaders/{name}_i{channel}.pngPopulate from extracted info:
name → info.nameshadertoyId → the ID from the URLauthor → info.usernamelicense → "CC BY-NC-SA 3.0" (Shadertoy default)iChannels → from downloaded textures, include filter/wrap from mSamplerClose the browser (browser_close) immediately — before writing any files or starting HLSL conversion. The Playwright MCP server is a shared resource; holding it open blocks other agents. Extract all data into local variables in Steps 2-5, then close the browser as the very first action in this step.
Proceed to HLSL conversion (same as Mode C).
After Mode B is fully complete (conversion, bundle, compile, tests), remind the user:
"Playwright MCP is still enabled and consuming context. To disable it for future sessions, run:"
powershell -File tools/toggle-playwright-mcp.ps1 off
src/shaders/name.glsl with the pasted sourcesrc/shaders/name.json with metadata (prompt for Shadertoy URL/author if not provided)src/shaders/name.hlsl| GLSL | HLSL |
|---|---|
vec2, vec3, vec4 | float2, float3, float4 |
ivec2, ivec3, ivec4 | int2, int3, int4 |
mat2, mat3, mat4 | float2x2, float3x3, float4x4 |
fract() | frac() |
mod(a, b) | fmod(a, b) |
mix(a, b, t) | lerp(a, b, t) |
texture(sampler, uv) | tex.Sample(samplerState, uv) |
texelFetch(sampler, coord, lod) | tex.Load(int3(coord, lod)) |
atan(y, x) | atan2(y, x) |
dFdx(), dFdy() | ddx(), ddy() |
GLSL allows vec3(x) as shorthand for vec3(x, x, x). HLSL does too with float3(x, x, x) or (float3)x.
| Shadertoy | HLSL cbuffer |
|---|---|
iTime | time |
iResolution.xy | resolution |
iResolution (vec3) | float3(resolution, 1.0) |
iTimeDelta | timeDelta |
iFrame | frame |
fragCoord | input.pos.xy (from SV_Position) |
fragColor | return value of PSMain |
Replace void mainImage(out vec4 fragColor, in vec2 fragCoord) with:
// PSInput and cbuffer are provided by alt_tabby_common.hlsl (prepended automatically)
float4 PSMain(PSInput input) : SV_Target {
float2 fragCoord = input.pos.xy;
// ... converted body ...
return AT_PostProcess(color); // instead of manual post-processing
}
Y-axis flip: Shadertoy's fragCoord.y = 0 is at the bottom of the screen; SV_Position.y = 0 is at the top. For shaders with gravity, falling particles, directional motion, or any up/down asymmetry, flip Y at the start:
float2 fragCoord = float2(input.pos.x, resolution.y - input.pos.y);
Symmetric shaders (noise fields, clouds, fractals) usually don't need the flip.
The cbuffer and PSInput struct are provided by alt_tabby_common.hlsl, which is prepended automatically before compilation. Do NOT include them in the .hlsl file. The common header provides:
cbuffer Constants : register(b0) with all uniforms (144 bytes, 9 × 16-byte rows):
time (float), resolution (float2), timeDelta (float), frame (uint), darken (float), desaturate (float), opacity (float)iMouse (float2, cursor px), iMouseVel (float2, velocity px/sec), iMouseSpeed (float, magnitude of velocity)gridW (uint, grid width, 0 = no grid), gridH (uint, grid height, 0 = no grid), maxParticles (uint, particle slots excluding grid cells), reactivity (float, cursor force multiplier)selRect (float4, x/y/w/h), selColor (float4, premul RGBA), borderColor (float4, premul RGBA), borderWidth (float), isHovered (float, intensity: 1.0 = full selected, <1.0 = dimmed for hover), entranceT (float), selGlow (float, outer glow radius multiplier), selIntensity (float, effect blend strength), rowRadius (float, user's RowRadius in pixels, 0 = shader decides)struct PSInput with SV_Position and TEXCOORD0AT_PostProcess(float3 col) and AT_PostProcess(float3 col, float customAlpha) functionsAll fields are populated every frame for all shader categories. Background shaders typically use only core fields. Mouse shaders use core + mouse fields. Selection shaders use core + selection fields.
Compute-enabled mouse shaders access the same cbuffer via register(b0) in both CSMain and PSMain. The common header is prepended for both entry points during compilation.
iChannel Texture2D/SamplerState declarations still go in the individual .hlsl file (they vary per shader and are NOT in the common header).
Standard shaders (alpha from brightness): End PSMain with return AT_PostProcess(color);
Shaders with custom alpha (transparency masks, particle alpha, etc.): End PSMain with return AT_PostProcess(color, customAlpha);
AT_PostProcess handles darken, desaturate, opacity multiplication, and premultiplied alpha output. Do NOT write these manually.
Shadertoy shaders may use iChannel0..3 for audio input (spectrum/waveform) or produce audio output via a "Sound" tab.
Audio input (e.g., texture(iChannel0, vec2(freq, 0.0)).r for spectrum): Replace with a gentle time-based pulse so the shader retains dynamic variation without requiring audio hardware:
float getBeat() {
return smoothstep(0.6, 0.9, pow(sin(time * 1.5) * 0.5 + 0.5, 4.0)) * 0.3;
}
Adjust frequency/amplitude to match how the original used the audio data (subtle background pulse vs heavy bass reactivity).
Audio output ("Sound" tab shaders): Remove entirely. Alt-Tabby is visual only.
Shadertoy provides iMouse (pixel coordinates, click state). Alt-Tabby provides mouse data via cbuffer:
iMouse (float2): cursor position in physical pixelsiMouseVel (float2): cursor velocity in pixels/second (smoothed, CPU-computed)iMouseSpeed (float): magnitude of iMouseVel (convenience scalar)For background shaders (no mouse interaction): Zero out or ignore iMouse. Set any derived mouse variables to (float2)0 and simplify away dead code. If mouse is the sole camera control, replace with a time-based sweep:
float2 fakeMouse = float2(
sin(time * 0.1) * 0.3,
cos(time * 0.07) * 0.2
);
For mouse-category shaders: Use iMouse for cursor position, iMouseVel for direction-dependent effects (particles trailing behind cursor), and iMouseSpeed for motion gating (e.g., if (iMouseSpeed < threshold) return zero). Speed-gated effects should use smoothstep() for gradual activation rather than hard cutoffs.
If the shader uses iChannel0..3:
src/shaders/name_i0.png, name_i1.png, etc..json metadata:
"iChannels": [{"index": 0, "file": "name_i0.png", "filter": "linear", "wrap": "repeat"}]
Texture2D iChannel0 : register(t0);
SamplerState samp0 : register(s0);
texture(iChannelN, uv) with iChannelN.Sample(sampN, uv)Run the bundle script:
powershell -File tools/shader_bundle.ps1
Compile shaders to DXBC:
powershell -File tools/shader_compile.ps1
Run tests:
.\tests\test.ps1
{
"name": "Display Name",
"shadertoyId": "XXXXXX",
"author": "Author Name",
"license": "CC BY-NC-SA 3.0",
"opacity": 0.50,
"iChannels": [],
"timeOffsetMin": 40,
"timeOffsetMax": 120,
"timeAccumulate": true
}
opacity: Default layer opacity when compositing (0.0-1.0)iChannels: Array of texture references (empty if no textures needed)timeOffsetMin: (optional) Minimum random time offset in seconds. Skips the shader's warmup period so it looks interesting immediately. Falls back to config ShaderTimeOffsetMin (default 30) if omitted.timeOffsetMax: (optional) Maximum random time offset in seconds. Falls back to config ShaderTimeOffsetMax (default 90) if omitted. Set higher for shaders with long warmup (e.g., volumetric fog needs 40-120s).timeAccumulate: (optional) When true, shader time persists across overlay show/hide so it picks up where it left off. Falls back to config ShaderTimeAccumulate (default true) if omitted. Set false for shaders with a deliberate intro animation you want to see each time.category: (optional) "mouse" or "selection" for shaders in subdirectories. Background shaders (root dir) omit this field.compute: (optional) { "maxParticles": N, "particleStride": 32, "baseParticles": M }. When present, the shader is compiled as a compute+pixel pair. CS entry point is CSMain (compiled with cs_5_0), PS entry point is PSMain (compiled with ps_5_0), both in the same .hlsl file. The compute shader writes to RWStructuredBuffer via UAV; the pixel shader reads the same buffer as StructuredBuffer at register(t4). Only used for mouse-category shaders that need persistent GPU-side state (particles, waves).
baseParticles is the shader's intrinsic particle count (0 for pure-grid fluids, 128/384 for particle+grid). maxParticles in the JSON is the default total (= baseParticles + gridW * gridH at high quality). At runtime, actual buffer size is computed from baseParticles * ParticleDensity + gridW * gridH where grid dimensions come from GridQuality config.Add time fields when the shader has a notable warmup period or deliberate intro. Omit them for shaders that look good immediately at any time value.
Note on shader models: Compute shaders require cs_5_0 profile (DX11 feature level 11_0). Compute-paired pixel shaders use ps_5_0. Background and selection shaders remain ps_4_0 for maximum compatibility.
Compute-enabled mouse shaders follow one of three buffer layout patterns:
[particles 0..N-1] [grid cells N..N+W*H-1]. CS particle threads do physics, CS grid threads accumulate nearby particles. PS bilinear-samples the grid. Grid cells reuse the Particle struct: pos.xy = accumulated RG, vel.xy = accumulated BA.[grid cells 0..W*H-1]. CS runs wave/fluid equations. PS samples the grid.[slots 0..N-1]. CS manages slot lifecycle. PS loops over all slots.Shaders are organized into three categories by directory:
src/shaders/): Composited as stackable layers behind the window list. Up to 4 layers.src/shaders/mouse/): Single-slot effect receiving cursor data: iMouse (position), iMouseVel (velocity px/sec), iMouseSpeed (speed magnitude). Add "category": "mouse" to JSON.src/shaders/selection/): Single-slot effect for row selection highlight. Receives selRect, selColor, borderColor, borderWidth, isHovered, entranceT, selGlow, selIntensity, rowRadius via cbuffer. isHovered is an intensity multiplier (1.0 = full selected, <1.0 = dimmed for hover reuse) — use it directly as float intensity = isHovered;. Do NOT hardcode hover dimming (e.g., lerp(1.0, 0.45, isHovered) is wrong). For corner radius, use rowRadius > 0.0 ? rowRadius : min(hs.x, hs.y) * 0.15 so the shader respects the user's RowRadius setting while falling back to 15% of the smaller half-dimension. Add "category": "selection" to JSON.