{"id":25499952,"url":"https://github.com/alexeykarnachev/py2glsl","last_synced_at":"2025-08-07T15:18:54.595Z","repository":{"id":276865143,"uuid":"930561915","full_name":"alexeykarnachev/py2glsl","owner":"alexeykarnachev","description":"Simple to use Python to GLSL transpiler","archived":false,"fork":false,"pushed_at":"2025-02-18T22:14:18.000Z","size":1117,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-18T22:23:37.650Z","etag":null,"topics":["compiler","dsl","glsl","opengl","programming-language","python","shader","shader-programming","shaders","shadertoy","transpiler"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alexeykarnachev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-02-10T20:45:22.000Z","updated_at":"2025-02-11T00:00:03.000Z","dependencies_parsed_at":"2025-02-10T21:35:05.254Z","dependency_job_id":"2dc7c721-e334-48e9-b337-53199eb7afe0","html_url":"https://github.com/alexeykarnachev/py2glsl","commit_stats":null,"previous_names":["alexeykarnachev/py2glsl"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexeykarnachev%2Fpy2glsl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexeykarnachev%2Fpy2glsl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexeykarnachev%2Fpy2glsl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexeykarnachev%2Fpy2glsl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alexeykarnachev","download_url":"https://codeload.github.com/alexeykarnachev/py2glsl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239590744,"owners_count":19664546,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["compiler","dsl","glsl","opengl","programming-language","python","shader","shader-programming","shaders","shadertoy","transpiler"],"created_at":"2025-02-19T03:28:26.651Z","updated_at":"2025-08-07T15:18:54.588Z","avatar_url":"https://github.com/alexeykarnachev.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# py2glsl 🎨\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"thumbnail.gif\" alt=\"py2glsl\" width=\"600\"/\u003e\n\u003c/p\u003e\n\nTransform Python functions into GLSL shaders with zero boilerplate.\nWrite complex shaders in pure Python with type hinting,\nincluding custom structs and global constants, then render them as real-time animations,\nimages, GIFs, or videos—all with proper IDE support and no GLSL knowledge required\n(though it helps!).\n\n## Quick Start\n\nInstall using uv to get both the library and command-line tool:\n\n```bash\nuv pip install py2glsl\n```\n\nCreate a simple animated shader file `plasma.py`:\n\n```python\nfrom py2glsl.builtins import length, sin, vec2, vec4\n\ndef shader(vs_uv: vec2, u_time: float, u_aspect: float) -\u003e vec4:\n    \"\"\"A simple animated plasma shader.\"\"\"\n    uv = vs_uv * 2.0 - 1.0  # Center UV coordinates\n    d = length(uv)\n    color = sin(d * 10.0 - u_time * 2.0) * 0.5 + 0.5\n    return vec4(color, color * 0.5, 1.0 - color, 1.0)\n```\n\nRun it using the command-line interface:\n\n```bash\n# Interactive preview\npy2glsl show run plasma.py\n\n# Save as image\npy2glsl image render plasma.py output.png\n\n# Create animated GIF\npy2glsl gif render plasma.py animation.gif --duration 5.0\n\n# Specify a particular function to use (default is 'shader')\npy2glsl show run plasma.py --main my_custom_shader\n\n# Export code for Shadertoy\npy2glsl code export plasma.py shadertoy.glsl --target shadertoy --format wrapped\n\n# Export Shadertoy-compatible code (removes built-in uniforms)\npy2glsl code export plasma.py shadertoy_ready.glsl --target shadertoy --format wrapped --shadertoy-compatible\n```\n\nOr use the library directly in your code:\n\n```python\nfrom py2glsl.render import animate\nfrom plasma import plasma  # Import your shader function\n\n# Run real-time animation at 30fps\nanimate(plasma, fps=30)\n```\n\n## Features\n\n- **Python-to-GLSL Transpilation**: Write shaders in Python with full type hinting,\n  custom structs, and global constants—automatically converted to GLSL.\n- **Built-in GLSL Functions**: Use familiar functions like sin, cos, length, normalize,\n  and more directly in Python.\n- **Command-Line Interface**:\n  - Interactive preview with `py2glsl show`\n  - Static image rendering with `py2glsl image`  \n  - Video rendering with `py2glsl video`\n  - GIF creation with `py2glsl gif`\n  - Code export with `py2glsl code` (includes Shadertoy-compatibility mode)\n- **Flexible Rendering API**:\n  - Real-time animations with `animate()` (with framerate control)\n  - Static images with `render_image()`\n  - Animated GIFs with `render_gif()`\n  - Videos with `render_video()`\n- **Multiple Target Languages**:\n  - Standard GLSL\n  - Shadertoy\n  - More coming soon (HLSL, WGSL)\n- **IDE-Friendly**: Leverages Python's type system for autocompletion and error checking.\n- **No GLSL Boilerplate**: Focus on shader logic without writing vertex/fragment wrappers.\n\n## Installation\n\nFor users:\n\n```bash\n# Using uv (recommended)\nuv pip install py2glsl\n\n# From source with uv\nuv pip install git+https://github.com/alexeykarnachev/py2glsl.git\n\n# Using pipx (for command-line usage only)\npipx install py2glsl\n```\n\nFor development:\n\n```bash\ngit clone https://github.com/alexeykarnachev/py2glsl.git\ncd py2glsl\nuv venv\nsource .venv/bin/activate  # or .venv/Scripts/activate on Windows\nuv sync\n```\n\nInstall pre-commit hooks:\n\n```bash\nuv pip install pre-commit\npre-commit install\n```\n\n## Command-Line Interface\n\npy2glsl provides a comprehensive command-line interface for working with shaders.\n\n### Interactive Preview\n\n```bash\npy2glsl show run shader_file.py [OPTIONS]\n\nOptions:\n  -t, --target TEXT      Target language (glsl, shadertoy)  [default: glsl]\n  -m, --main TEXT        Specific shader function to use\n  -w, --width INTEGER    Window width  [default: 800]\n  -h, --height INTEGER   Window height  [default: 600]\n  --fps INTEGER          Target framerate (0 for unlimited)  [default: 30]\n```\n\n### Render to Image\n\n```bash\npy2glsl image render shader_file.py output.png [OPTIONS]\n\nOptions:\n  -t, --target TEXT      Target language (glsl, shadertoy)  [default: glsl]\n  -m, --main TEXT        Specific shader function to use\n  -w, --width INTEGER    Image width  [default: 800]\n  -h, --height INTEGER   Image height  [default: 600]\n  --time FLOAT           Time value for the image  [default: 0.0]\n```\n\n### Render to Video\n\n```bash\npy2glsl video render shader_file.py output.mp4 [OPTIONS]\n\nOptions:\n  -t, --target TEXT      Target language (glsl, shadertoy)  [default: glsl]\n  -m, --main TEXT        Specific shader function to use\n  -w, --width INTEGER    Video width  [default: 800]\n  -h, --height INTEGER   Video height  [default: 600]\n  --fps INTEGER          Frames per second  [default: 30]\n  -d, --duration FLOAT   Duration in seconds  [default: 5.0]\n  --time-offset FLOAT    Starting time for animation  [default: 0.0]\n  --codec TEXT           Video codec (h264, vp9, etc.)  [default: h264]\n  -q, --quality INTEGER  Video quality (0-10)  [default: 8]\n```\n\n### Render to GIF\n\n```bash\npy2glsl gif render shader_file.py output.gif [OPTIONS]\n\nOptions:\n  -t, --target TEXT      Target language (glsl, shadertoy)  [default: glsl]\n  -m, --main TEXT        Specific shader function to use\n  -w, --width INTEGER    GIF width  [default: 800]\n  -h, --height INTEGER   GIF height  [default: 600]\n  --fps INTEGER          Frames per second  [default: 30]\n  -d, --duration FLOAT   Duration in seconds  [default: 5.0]\n  --time-offset FLOAT    Starting time for animation  [default: 0.0]\n```\n\n### Export Shader Code\n\n```bash\npy2glsl code export shader_file.py output.glsl [OPTIONS]\n\nOptions:\n  -t, --target TEXT           Target language (glsl, shadertoy)  [default: glsl]\n  -m, --main TEXT             Specific shader function to use\n  -f, --format TEXT           Code format (plain, commented, wrapped)  [default: plain]\n  -s, --shadertoy-compatible  Process code for direct Shadertoy paste (removes version and uniforms)\n```\n\n## Library Usage\n\n### Basic Shader\n\n```python\nfrom py2glsl.builtins import length, smoothstep, vec2, vec4\nfrom py2glsl.render import render_image\n\n\ndef main(vs_uv: vec2, u_time: float, u_aspect: float) -\u003e vec4:\n    \"\"\"A static circle shader.\"\"\"\n    d = length(vs_uv * 2.0 - 1.0)\n    color = 1.0 - smoothstep(0.0, 0.01, d - 0.5)\n    return vec4(color, color, color, 1.0)\n\n\n# Save as PNG\nrender_image(main).save(\"circle.png\")\n```\n\n### Animated Shader (GIF)\n```python\nfrom py2glsl.builtins import length, sin, vec2, vec4\nfrom py2glsl.render import render_gif\nfrom py2glsl.transpiler.backends.models import BackendType\n\n\ndef main(vs_uv: vec2, u_time: float, u_aspect: float) -\u003e vec4:\n    \"\"\"An animated ripple effect.\"\"\"\n    uv = vs_uv * 2.0 - 1.0\n    d = length(uv)\n    wave = sin(d * 10.0 - u_time * 2.0) * 0.5 + 0.5\n    return vec4(wave, wave * 0.5, 1.0 - wave, 1.0)\n\n\n# Create animated GIF\n_, frames = render_gif(main, duration=2.0, fps=30, output_path=\"ripple.gif\")\n\n# For Shadertoy compatibility:\n_, frames = render_gif(main, duration=2.0, fps=30, output_path=\"ripple.gif\", \n                      backend_type=BackendType.SHADERTOY)\n```\n\n### Advanced Example: Ray Marching\n\nHere's a more complex example using ray marching with structs and global constants:\n\n```python\nfrom dataclasses import dataclass\n\nfrom py2glsl.builtins import length, sin, vec2, vec3, vec4, normalize\nfrom py2glsl.render import animate\nfrom py2glsl.transpiler import transpile\nfrom py2glsl.transpiler.core.interfaces import TargetLanguageType\n\n# Global constants\nPI: float = 3.141592\nRM_MAX_DIST: float = 10000.0\nRM_MAX_STEPS: int = 64\nRM_EPS: float = 0.0001\n\n\n@dataclass\nclass RayMarchResult:\n    steps: int\n    p: vec3\n    normal: vec3\n    ro: vec3\n    rd: vec3\n    dist: float\n    sd_last: float\n    sd_min: float\n    sd_min_shape: float\n    has_normal: bool\n\n\ndef get_sd_shape(p: vec3) -\u003e float:\n    \"\"\"Signed distance to a sphere.\"\"\"\n    return length(p) - 1.0\n\n\ndef march(ro: vec3, rd: vec3) -\u003e RayMarchResult:\n    \"\"\"Ray marching function.\"\"\"\n    rm = RayMarchResult(\n        steps=0,\n        p=ro,\n        normal=vec3(0.0),\n        ro=ro,\n        rd=rd,\n        dist=0.0,\n        sd_last=0.0,\n        sd_min=RM_MAX_DIST,\n        sd_min_shape=RM_MAX_DIST,\n        has_normal=False,\n    )\n    for i in range(RM_MAX_STEPS):\n        rm.steps = i\n        rm.p = rm.p + rm.rd * rm.sd_last\n        rm.sd_last = get_sd_shape(rm.p)\n        rm.dist = rm.dist + length(rm.p - rm.ro)\n        if rm.sd_last \u003c RM_EPS or rm.dist \u003e RM_MAX_DIST:\n            break\n    return rm\n\n\ndef main(vs_uv: vec2, u_time: float, u_aspect: float) -\u003e vec4:\n    \"\"\"Ray-marched sphere with animation.\"\"\"\n    ro = vec3(0.0, 0.0, 5.0 + sin(u_time))\n    rd = normalize(vec3(vs_uv * 2.0 - 1.0, -1.0))\n    rm = march(ro, rd)\n    color = vec3(0.1, 0.2, 0.3)  # Background\n    if rm.sd_last \u003c RM_EPS:\n        color = vec3(1.0, 0.5, 0.2)  # Hit color\n    return vec4(color, 1.0)\n\n\n# Transpile with constants and structs\nglsl_code, _ = transpile(\n    march,\n    get_sd_shape,\n    main,\n    RayMarchResult,\n    PI=PI,\n    RM_MAX_DIST=RM_MAX_DIST,\n    RM_MAX_STEPS=RM_MAX_STEPS,\n    RM_EPS=RM_EPS,\n    # Optional: specify main function - defaults to \"main\" \n    # main_func=\"main\",\n    # Optional: specify target language\n    target_type=TargetLanguageType.GLSL  # or TargetLanguageType.SHADERTOY\n)\n# Run animation with 30fps limit\nanimate(glsl_code, fps=30)\n```\n\n### Debugging GLSL Output\n\n```python\nfrom py2glsl.builtins import vec2, vec4\nfrom py2glsl.transpiler import transpile\nfrom py2glsl.transpiler.core.interfaces import TargetLanguageType\n\n\ndef main(vs_uv: vec2, u_time: float, u_aspect: float) -\u003e vec4:\n    return vec4(vs_uv, 0.0, 1.0)\n\n\nglsl_code, uniforms = transpile(main)\nprint(\"Fragment Shader:\")\nprint(glsl_code)\n\n# Transpile to Shadertoy format\nshadertoy_code, shadertoy_uniforms = transpile(\n    main, \n    target_type=TargetLanguageType.SHADERTOY\n)\nprint(\"Shadertoy Fragment Shader:\")\nprint(shadertoy_code)\n```\n\n### Advanced Rendering Options\n\n```python\nfrom py2glsl.builtins import vec2, vec4, sin, length\nfrom py2glsl.render import animate, render_video, render_gif\nfrom py2glsl.transpiler.backends.models import BackendType\n\ndef main(vs_uv: vec2, u_time: float, u_aspect: float) -\u003e vec4:\n    \"\"\"Simple animated color shader.\"\"\"\n    d = length(vs_uv * 2.0 - 1.0)\n    c = sin(d * 10.0 - u_time * 2.0) * 0.5 + 0.5\n    return vec4(c, c * 0.5, 1.0 - c, 1.0)\n\n# Real-time animation with frame rate control\nanimate(main, fps=30)  # Cap at 30fps\nanimate(main, fps=0)   # Unlimited frame rate (default)\n\n# Interactive animation with Shadertoy compatibility\nanimate(main, backend_type=BackendType.SHADERTOY, fps=60)\n\n# Video rendering with different settings\nrender_video(\n    main,\n    size=(1920, 1080),  # Full HD\n    duration=5.0,       # 5 seconds\n    fps=60,             # 60 frames per second\n    output_path=\"shader.mp4\",\n    codec=\"h264\",       # Video codec\n    quality=8,          # Quality level (0-10)\n    backend_type=BackendType.STANDARD,  # Use GLSL standard format\n)\n\n# GIF with custom parameters\nrender_gif(\n    main,\n    size=(600, 600),    # Square dimensions\n    duration=3.0,       # 3 seconds loop\n    fps=24,             # 24 frames per second\n    output_path=\"shader.gif\",\n    time_offset=1.0,    # Start animation from time=1.0\n)\n```\n\n## License\n\nMIT License - see [LICENSE](./LICENSE) for details.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexeykarnachev%2Fpy2glsl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexeykarnachev%2Fpy2glsl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexeykarnachev%2Fpy2glsl/lists"}