PyCon 2022
Paul Vincent Craven
https://tinyurl.com/pythongpu
https://tinyurl.com/pythongpu
https://tinyurl.com/pythongpu
draw_rect()
draw_rect()
https://tinyurl.com/pythongpu
send_data()
https://tinyurl.com/pythongpu
draw_all()
https://tinyurl.com/pythongpu
https://tinyurl.com/pythongpu
send_data()
send_script()
https://tinyurl.com/pythongpu
https://tinyurl.com/pythongpu
https://tinyurl.com/pythongpu
https://tinyurl.com/pythongpu
import arcade
# Derive an application window from Arcade's parent Window class
class MyGame(arcade.Window):
def __init__(self):
# Call the parent constructor
super().__init__(width=1920, height=1080)
def on_draw(self):
# Clear the screen
self.clear()
if __name__ == "__main__":
MyGame()
arcade.run()
import arcade
# Derive an application window from Arcade's parent Window class
class MyGame(arcade.Window):
def __init__(self):
# Call the parent constructor
super().__init__(width=1920, height=1080)
def on_draw(self):
# Clear the screen
self.clear()
if __name__ == "__main__":
MyGame()
arcade.run()
import arcade
# Derive an application window from Arcade's parent Window class
class MyGame(arcade.Window):
def __init__(self):
# Call the parent constructor
super().__init__(width=1920, height=1080)
def on_draw(self):
# Clear the screen
self.clear()
if __name__ == "__main__":
MyGame()
arcade.run()
import arcade
# Derive an application window from Arcade's parent Window class
class MyGame(arcade.Window):
def __init__(self):
# Call the parent constructor
super().__init__(width=1920, height=1080)
def on_draw(self):
# Clear the screen
self.clear()
if __name__ == "__main__":
MyGame()
arcade.run()
import arcade
# Derive an application window from Arcade's parent Window class
class MyGame(arcade.Window):
def __init__(self):
# Call the parent constructor
super().__init__(width=1920, height=1080)
def on_draw(self):
# Clear the screen
self.clear()
if __name__ == "__main__":
MyGame()
arcade.run()
import arcade
# Derive an application window from Arcade's parent Window class
class MyGame(arcade.Window):
def __init__(self):
# Call the parent constructor
super().__init__(width=1920, height=1080)
def on_draw(self):
# Clear the screen
self.clear()
if __name__ == "__main__":
MyGame()
arcade.run()
import arcade
# Derive an application window from Arcade's parent Window class
class MyGame(arcade.Window):
def __init__(self):
# Call the parent constructor
super().__init__(width=1920, height=1080)
def on_draw(self):
# Clear the screen
self.clear()
if __name__ == "__main__":
MyGame()
arcade.run()
import arcade
# Derive an application window from Arcade's parent Window class
class MyGame(arcade.Window):
def __init__(self):
# Call the parent constructor
super().__init__(width=1920, height=1080)
def on_draw(self):
# Clear the screen
self.clear()
if __name__ == "__main__":
MyGame()
arcade.run()
import arcade
# Derive an application window from Arcade's parent Window class
class MyGame(arcade.Window):
def __init__(self):
# Call the parent constructor
super().__init__(width=1920, height=1080)
def on_draw(self):
# Clear the screen
self.clear()
if __name__ == "__main__":
MyGame()
arcade.run()
https://tinyurl.com/pythongpu
import arcade
from arcade.experimental import Shadertoy
# Derive an application window from Arcade's parent Window class
class MyGame(arcade.Window):
def __init__(self):
# Call the parent constructor
super().__init__(width=1920, height=1080)
# Load a file and create a shader from it
shader_path = "circle_1.glsl"
size = self.get_size()
self.shadertoy = Shadertoy.create_from_file(size, shader_path)
def on_draw(self):
# Run the GLSL code
self.shadertoy.render()
if __name__ == "__main__":
MyGame()
arcade.run()
import arcade
from arcade.experimental import Shadertoy
# Derive an application window from Arcade's parent Window class
class MyGame(arcade.Window):
def __init__(self):
# Call the parent constructor
super().__init__(width=1920, height=1080)
# Load a file and create a shader from it
shader_path = "circle_1.glsl"
size = self.get_size()
self.shadertoy = Shadertoy.create_from_file(size, shader_path)
def on_draw(self):
# Run the GLSL code
self.shadertoy.render()
if __name__ == "__main__":
MyGame()
arcade.run()
import arcade
from arcade.experimental import Shadertoy
# Derive an application window from Arcade's parent Window class
class MyGame(arcade.Window):
def __init__(self):
# Call the parent constructor
super().__init__(width=1920, height=1080)
# Load a file and create a shader from it
shader_path = "circle_1.glsl"
size = self.get_size()
self.shadertoy = Shadertoy.create_from_file(size, shader_path)
def on_draw(self):
# Run the GLSL code
self.shadertoy.render()
if __name__ == "__main__":
MyGame()
arcade.run()
If we are close to the origin (0, 0):
draw white
else
draw black
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// How far is the current pixel from the origin (0, 0)
float distance = length(uv);
// Are we are 20% of the screen away from the origin?
if (distance > 0.2) {
// Black
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
} else {
// White
fragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// How far is the current pixel from the origin (0, 0)
float distance = length(uv);
// Are we are 20% of the screen away from the origin?
if (distance > 0.2) {
// Black
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
} else {
// White
fragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// How far is the current pixel from the origin (0, 0)
float distance = length(uv);
// Are we are 20% of the screen away from the origin?
if (distance > 0.2) {
// Black
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
} else {
// White
fragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// How far is the current pixel from the origin (0, 0)
float distance = length(uv);
// Are we are 20% of the screen away from the origin?
if (distance > 0.2) {
// Black
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
} else {
// White
fragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// How far is the current pixel from the origin (0, 0)
float distance = length(uv);
// Are we are 20% of the screen away from the origin?
if (distance > 0.2) {
// Black
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
} else {
// White
fragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// How far is the current pixel from the origin (0, 0)
float distance = length(uv);
// Are we are 20% of the screen away from the origin?
if (distance > 0.2) {
// Black
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
} else {
// White
fragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// How far is the current pixel from the origin (0, 0)
float distance = length(uv);
// Are we are 20% of the screen away from the origin?
if (distance > 0.2) {
// Black
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
} else {
// White
fragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// How far is the current pixel from the origin (0, 0)
float distance = length(uv);
// Are we are 20% of the screen away from the origin?
if (distance > 0.2) {
// Black
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
} else {
// White
fragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// How far is the current pixel from the origin (0, 0)
float distance = length(uv);
// Are we are 20% of the screen away from the origin?
if (distance > 0.2) {
// Black
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
} else {
// White
fragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// How far is the current pixel from the origin (0, 0)
float distance = length(uv);
// Are we are 20% of the screen away from the origin?
if (distance > 0.2) {
// Black
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
} else {
// White
fragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// How far is the current pixel from the origin (0, 0)
float distance = length(uv);
// Are we are 20% of the screen away from the origin?
if (distance > 0.2) {
// Black
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
} else {
// White
fragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// How far is the current pixel from the origin (0, 0)
float distance = length(uv);
// Are we are 20% of the screen away from the origin?
if (distance > 0.2) {
// Black
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
} else {
// White
fragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
}
https://tinyurl.com/pythongpu
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// Position of fragment relative to center of screen
vec2 rpos = uv - 0.5;
// Adjust y by aspect ratio
rpos.y /= iResolution.x/iResolution.y;
// How far is the current pixel from the origin (0, 0)
float distance = length(rpos);
// Default our color to white
vec3 color = vec3(1.0, 1.0, 1.0);
// Are we are 20% of the screen away from the origin?
if (distance > 0.2) {
// Black
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
} else {
// White
fragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// Position of fragment relative to center of screen
vec2 rpos = uv - 0.5;
// Adjust y by aspect ratio
rpos.y /= iResolution.x/iResolution.y;
// How far is the current pixel from the origin (0, 0)
float distance = length(rpos);
// Default our color to white
vec3 color = vec3(1.0, 1.0, 1.0);
// Are we are 20% of the screen away from the origin?
if (distance > 0.2) {
// Black
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
} else {
// White
fragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// Position of fragment relative to center of screen
vec2 rpos = uv - 0.5;
// Adjust y by aspect ratio
rpos.y /= iResolution.x/iResolution.y;
// How far is the current pixel from the origin (0, 0)
float distance = length(rpos);
// Default our color to white
vec3 color = vec3(1.0, 1.0, 1.0);
// Are we are 20% of the screen away from the origin?
if (distance > 0.2) {
// Black
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
} else {
// White
fragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
}
https://tinyurl.com/pythongpu
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// Position of fragment relative to center of screen
vec2 rpos = uv - 0.5;
// Adjust y by aspect ratio
rpos.y /= iResolution.x/iResolution.y;
// How far is the current pixel from the origin (0, 0)
float distance = length(rpos);
// Use an inverse 1/distance to set the fade
float scale = 0.02;
float strength = 1.0 / distance * scale;
// Fade our white color
vec3 color = strength * vec3(1.0, 1.0, 1.0);
// Output to the screen
fragColor = vec4(color, 1.0);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// Position of fragment relative to center of screen
vec2 rpos = uv - 0.5;
// Adjust y by aspect ratio
rpos.y /= iResolution.x/iResolution.y;
// How far is the current pixel from the origin (0, 0)
float distance = length(rpos);
// Use an inverse 1/distance to set the fade
float scale = 0.02;
float strength = 1.0 / distance * scale;
// Fade our white color
vec3 color = strength * vec3(1.0, 1.0, 1.0);
// Output to the screen
fragColor = vec4(color, 1.0);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// Position of fragment relative to center of screen
vec2 rpos = uv - 0.5;
// Adjust y by aspect ratio
rpos.y /= iResolution.x/iResolution.y;
// How far is the current pixel from the origin (0, 0)
float distance = length(rpos);
// Use an inverse 1/distance to set the fade
float scale = 0.02;
float strength = 1.0 / distance * scale;
// Fade our white color
vec3 color = strength * vec3(1.0, 1.0, 1.0);
// Output to the screen
fragColor = vec4(color, 1.0);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// Position of fragment relative to center of screen
vec2 rpos = uv - 0.5;
// Adjust y by aspect ratio
rpos.y /= iResolution.x/iResolution.y;
// How far is the current pixel from the origin (0, 0)
float distance = length(rpos);
// Use an inverse 1/distance to set the fade
float scale = 0.02;
float strength = 1.0 / distance * scale;
// Fade our white color
vec3 color = strength * vec3(1.0, 1.0, 1.0);
// Output to the screen
fragColor = vec4(color, 1.0);
}
https://tinyurl.com/pythongpu
https://tinyurl.com/pythongpu
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// Position of fragment relative to center of screen
vec2 rpos = uv - 0.5;
// Adjust y by aspect ratio
rpos.y /= iResolution.x/iResolution.y;
// How far is the current pixel from the origin (0, 0)
float distance = length(rpos);
// Use an inverse 1/distance to set the fade
float scale = 0.02;
float fade = 1.5;
float strength = pow(1.0 / distance * scale, fade);
// Fade our orange color
vec3 color = strength * vec3(1.0, 0.5, 0.0);
// Output to the screen
fragColor = vec4(color, 1.0);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// Position of fragment relative to center of screen
vec2 rpos = uv - 0.5;
// Adjust y by aspect ratio
rpos.y /= iResolution.x/iResolution.y;
// How far is the current pixel from the origin (0, 0)
float distance = length(rpos);
// Use an inverse 1/distance to set the fade
float scale = 0.02;
float fade = 1.5;
float strength = pow(1.0 / distance * scale, fade);
// Fade our orange color
vec3 color = strength * vec3(1.0, 0.5, 0.0);
// Output to the screen
fragColor = vec4(color, 1.0);
}
1.5
1.0
0.5
uniform vec2 pos;
uniform vec3 color;
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
vec2 npos = pos/iResolution.xy;
// Position of fragment relative to specified position
vec2 rpos = npos - uv;
// Adjust y by aspect ratio
rpos.y /= iResolution.x/iResolution.y;
// How far is the current pixel from the origin (0, 0)
float distance = length(rpos);
// Use an inverse 1/distance to set the fade
float scale = 0.02;
float fade = 1.5;
float strength = pow(1.0 / distance * scale, fade);
// Fade our orange color
vec3 color = strength * color;
// Tone mapping
color = 1.0 - exp( -color );
// Output to the screen
fragColor = vec4(color, 1.0);
}
import arcade
from arcade.experimental import Shadertoy
# Derive an application window from Arcade's parent Window class
class MyGame(arcade.Window):
def __init__(self):
# Call the parent constructor
super().__init__(width=1920, height=1080)
# Load a file and create a shader from it
shader_path = "circle_6.glsl"
size = self.get_size()
self.shadertoy = Shadertoy.create_from_file(size, shader_path)
def on_draw(self):
# Set uniform data to send to the GLSL shader
position = self.mouse["x"], self.mouse["y"]
color = arcade.get_three_float_color(arcade.color.LIGHT_BLUE)
self.shadertoy.program['pos'] = position
self.shadertoy.program['color'] = color
# Run the GLSL code
self.shadertoy.render()
if __name__ == "__main__":
MyGame()
arcade.run()
import arcade
from arcade.experimental import Shadertoy
# Derive an application window from Arcade's parent Window class
class MyGame(arcade.Window):
def __init__(self):
# Call the parent constructor
super().__init__(width=1920, height=1080)
# Load a file and create a shader from it
shader_path = "circle_6.glsl"
size = self.get_size()
self.shadertoy = Shadertoy.create_from_file(size, shader_path)
def on_draw(self):
# Set uniform data to send to the GLSL shader
position = self.mouse["x"], self.mouse["y"]
color = arcade.get_three_float_color(arcade.color.LIGHT_BLUE)
self.shadertoy.program['pos'] = position
self.shadertoy.program['color'] = color
# Run the GLSL code
self.shadertoy.render()
if __name__ == "__main__":
MyGame()
arcade.run()
import arcade
from arcade.experimental import Shadertoy
# Derive an application window from Arcade's parent Window class
class MyGame(arcade.Window):
def __init__(self):
# Call the parent constructor
super().__init__(width=1920, height=1080)
# Load a file and create a shader from it
shader_path = "circle_6.glsl"
size = self.get_size()
self.shadertoy = Shadertoy.create_from_file(size, shader_path)
def on_draw(self):
# Set uniform data to send to the GLSL shader
position = self.mouse["x"], self.mouse["y"]
color = arcade.get_three_float_color(arcade.color.LIGHT_BLUE)
self.shadertoy.program['pos'] = position
self.shadertoy.program['color'] = color
# Run the GLSL code
self.shadertoy.render()
if __name__ == "__main__":
MyGame()
arcade.run()
uniform vec2 pos;
uniform vec3 color;
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
vec2 npos = pos/iResolution.xy;
// Position of fragment relative to specified position
vec2 rpos = npos - uv;
// Adjust y by aspect ratio
rpos.y /= iResolution.x/iResolution.y;
// How far is the current pixel from the origin (0, 0)
float distance = length(rpos);
// Use an inverse 1/distance to set the fade
float scale = 0.02;
float fade = 1.1;
float strength = pow(1.0 / distance * scale, fade);
// Fade our orange color
vec3 color = strength * color;
// Tone mapping
color = 1.0 - exp( -color );
// Output to the screen
fragColor = vec4(color, 1.0);
}
uniform vec2 pos;
uniform vec3 color;
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
vec2 npos = pos/iResolution.xy;
// Position of fragment relative to specified position
vec2 rpos = npos - uv;
// Adjust y by aspect ratio
rpos.y /= iResolution.x/iResolution.y;
// How far is the current pixel from the origin (0, 0)
float distance = length(rpos);
// Use an inverse 1/distance to set the fade
float scale = 0.02;
float fade = 1.1;
float strength = pow(1.0 / distance * scale, fade);
// Fade our orange color
vec3 color = strength * color;
// Tone mapping
color = 1.0 - exp( -color );
// Output to the screen
fragColor = vec4(color, 1.0);
}
uniform vec2 pos;
uniform vec3 color;
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
vec2 npos = pos/iResolution.xy;
// Position of fragment relative to specified position
vec2 rpos = npos - uv;
// Adjust y by aspect ratio
rpos.y /= iResolution.x/iResolution.y;
// How far is the current pixel from the origin (0, 0)
float distance = length(rpos);
// Use an inverse 1/distance to set the fade
float scale = 0.02;
float fade = 1.1;
float strength = pow(1.0 / distance * scale, fade);
// Fade our orange color
vec3 color = strength * color;
// Tone mapping
color = 1.0 - exp( -color );
// Output to the screen
fragColor = vec4(color, 1.0);
}
uniform vec2 pos;
uniform vec3 color;
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
vec2 npos = pos/iResolution.xy;
// Position of fragment relative to specified position
vec2 rpos = npos - uv;
// Adjust y by aspect ratio
rpos.y /= iResolution.x/iResolution.y;
// How far is the current pixel from the origin (0, 0)
float distance = length(rpos);
// Use an inverse 1/distance to set the fade
float scale = 0.02;
float fade = 1.1;
float strength = pow(1.0 / distance * scale, fade);
// Fade our orange color
vec3 color = strength * color;
// Tone mapping
color = 1.0 - exp( -color );
// Output to the screen
fragColor = vec4(color, 1.0);
}
uniform vec2 pos;
uniform vec3 color;
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
vec2 npos = pos/iResolution.xy;
// Position of mouse relative to fragment position
vec2 rpos = npos - uv;
// Adjust y by aspect ratio
rpos.y /= iResolution.x/iResolution.y;
// How far is the current pixel from the origin (0, 0)
float distance = length(rpos);
// Use an inverse 1/distance to set the fade
float scale = 0.02;
float fade = 1.1;
float strength = pow(1.0 / distance * scale, fade);
// Fade our orange color
vec3 color = strength * color;
// Tone mapping
color = 1.0 - exp( -color );
// Output to the screen
fragColor = vec4(color, 1.0);
}
https://tinyurl.com/pythongpu
Read more about using OpenGL in Arcade with OpenGL Notes
Learn to do a compute shader in Compute Shader Tutorial
https://tinyurl.com/pythongpu