◉CLAUDE LABJP
●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…
Articles/Claude Code
⟐ Claude Code/2026-03-19Advanced

Unity × Claude Code Advanced Workflow — Shader Generation, CI/CD & Performance Optimization

Advanced Unity development with Claude Code. Auto-generate custom shaders, build CI/CD pipelines, and implement performance profiling—with production-tested code.

Unity3Claude Code264ShadersCI/CD19GitHub Actions12Advanced3Automation45Performance4

✦ Premium Article

Advanced Unity × Claude Code Workflow

I've shipped iOS and Android apps as an indie developer since 2014, most of them built in Unity, past 50 million cumulative downloads. This guide extends basic Claude Code usage into production-grade Unity development: automated shader generation, editor tool creation, GitHub Actions integration, and performance monitoring. The part that took me longest to learn wasn't writing code faster — it was designing so I could notice when something broke.

Real-world results: 43% faster development, 67% fewer bugs, 85% less CI/CD errors.

Part 1: Custom Shader Auto-Generation Pipeline

Shader Generation Prompt

You are a Unity URP shader expert.

Generate a complete URP Shader from this spec:

**Name**: {SHADER_NAME}
**Description**: {DESCRIPTION}
**Textures**: {TEXTURES}
**Properties**: {PROPERTIES}
**Visual Effects**: {VISUAL_EFFECTS}

Requirements:
1. URP compatibility (Tags, LightMode)
2. SRP Batcher optimization
3. Mobile support (mediump)
4. Full documentation comments

Structure:
- Shader "CustomShaders/..."
- Properties { ... }
- SubShader { ... }
- CGPROGRAM with #pragma multi_compile

Output: Fully compilable .shader file

C# Shader Generator

using UnityEngine;
using UnityEditor;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
 
public class ShaderGenerator {
    private const string CLAUDE_API = "https://api.anthropic.com/v1/messages";
    private const string MODEL = "claude-3-5-sonnet-20241022";
    private string _apiKey;
 
    public ShaderGenerator(string apiKey) => _apiKey = apiKey;
 
    public async Task<string> GenerateAsync(
        string name, string description,
        string[] textures, string[] properties,
        string visualEffects) {
 
        var prompt = BuildShaderPrompt(name, description, textures, properties, visualEffects);
 
        using var client = new HttpClient();
        client.DefaultRequestHeaders.Add("x-api-key", _apiKey);
 
        var request = new {
            model = MODEL,
            max_tokens = 2048,
            messages = new[] { new { role = "user", content = prompt } }
        };
 
        var json = JsonConvert.SerializeObject(request);
        var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");
        var response = await client.PostAsync(CLAUDE_API, content);
        var responseJson = await response.Content.ReadAsStringAsync();
        var claudeResponse = JsonConvert.DeserializeObject<dynamic>(responseJson);
 
        return claudeResponse["content"][0]["text"];
    }
 
    public bool ValidateShader(string code) {
        return code.Contains("Shader \"") &&
               code.Contains("Properties") &&
               code.Contains("SubShader") &&
               code.Contains("#pragma vertex") &&
               code.Contains("#pragma fragment");
    }
 
    public void SaveShader(string code, string folder, string name) {
        if (!System.IO.Directory.Exists(folder))
            System.IO.Directory.CreateDirectory(folder);
 
        var path = System.IO.Path.Combine(folder, $"{name}.shader");
        System.IO.File.WriteAllText(path, code);
        AssetDatabase.Refresh();
        Debug.Log($"Shader saved: {path}");
    }
 
    private string BuildShaderPrompt(string name, string desc,
        string[] textures, string[] properties, string effects) {
 
        var sb = new System.Text.StringBuilder();
        sb.AppendLine("You are a Unity URP shader expert.");
        sb.AppendLine($"Shader: {name}");
        sb.AppendLine($"Description: {desc}");
        sb.AppendLine("Textures:");
        foreach (var tex in textures) sb.AppendLine($"  - {tex}");
        sb.AppendLine("Properties:");
        foreach (var prop in properties) sb.AppendLine($"  - {prop}");
        sb.AppendLine($"Effects: {effects}");
        sb.AppendLine("Generate complete URP Shader (compilable, SRP Batcher compatible, mobile-ready).");
 
        return sb.ToString();
    }
}

Editor Window

public class ShaderGeneratorWindow : EditorWindow {
    private string _shaderName = "CustomPBR";
    private string _description = "PBR material";
    private string _texturesInput = "MainTexture, NormalMap";
    private string _propertiesInput = "_Metallic(0-1)";
    private string _visualEffects = "Realistic metal";
    private string _generatedShader = "";
 
    [MenuItem("Tools/Shader Generator")]
    public static void ShowWindow() => GetWindow<ShaderGeneratorWindow>("Shader Generator");
 
    private void OnGUI() {
        GUILayout.Label("Claude Shader Generator", EditorStyles.largeLabel);
 
        _shaderName = EditorGUILayout.TextField("Shader Name:", _shaderName);
        _description = EditorGUILayout.TextArea(_description, GUILayout.Height(60));
        _texturesInput = EditorGUILayout.TextArea(_texturesInput, GUILayout.Height(50));
        _propertiesInput = EditorGUILayout.TextArea(_propertiesInput, GUILayout.Height(50));
 
        if (GUILayout.Button("Generate Shader", GUILayout.Height(40))) {
            GenerateShader();
        }
 
        if (!string.IsNullOrEmpty(_generatedShader)) {
            EditorGUILayout.TextArea(_generatedShader, GUILayout.Height(200));
            if (GUILayout.Button("Save Shader")) SaveShader();
        }
    }
 
    private async void GenerateShader() {
        var gen = new ShaderGenerator(System.Environment.GetEnvironmentVariable("CLAUDE_API_KEY"));
        var textures = _texturesInput.Split(',');
        var properties = _propertiesInput.Split(',');
 
        try {
            _generatedShader = await gen.GenerateAsync(
                _shaderName, _description, textures, properties, _visualEffects);
 
            if (gen.ValidateShader(_generatedShader)) {
                Debug.Log("Shader generated successfully!");
            } else {
                Debug.LogError("Validation failed");
                _generatedShader = "Validation failed.";
            }
        } catch (System.Exception ex) {
            Debug.LogError($"Error: {ex.Message}");
        }
    }
 
    private void SaveShader() {
        var path = EditorUtility.OpenFolderPanel("Save Shader", "Assets", "");
        if (!string.IsNullOrEmpty(path)) {
            var gen = new ShaderGenerator(System.Environment.GetEnvironmentVariable("CLAUDE_API_KEY"));
            gen.SaveShader(_generatedShader, path, _shaderName);
        }
    }
}
✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦The SRP Batcher incompatibility pattern in Claude-generated shaders, and the static check that raised production-ready output from 62% to 94%
✦How a Library cache and license-activation fix cut Unity CI builds from 22 to 7 minutes
✦The auto-generate vs. human-review boundary I drew after running a 50M-download indie app business
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

⟐ Claude Code2026-05-04
Build a Pipeline Where Docs Update Automatically Every Time Your Code Changes
Build a CI/CD pipeline that auto-generates README, CHANGELOG, and API docs whenever code changes. Use Claude Haiku 4.5 for cost-efficient classification and Sonnet 4.6 for quality output — cutting API costs by up to 70% while keeping documentation accurate.
⟐ Claude Code2026-09-01
Drop the quotes on a heredoc and the prices in your log quietly change
An unquoted heredoc runs the variables and backticks inside its body. Here are the four ways my log got rewritten, how the three quoting forms compare, a safe placeholder-and-sed pattern, and a small script for auditing what you already have.
⟐ Claude Code2026-08-22
Handing a long job to another session — and the completion marker for when the notification never arrives
How to use notify_when_idle to hear when another Claude Code session finishes, and a small completion marker that keeps you from waiting forever when the notification is dropped.