diff --git a/Sources/backends/cstyle.c b/Sources/backends/cstyle.c
index 38e50d4..4ab5ba1 100644
--- a/Sources/backends/cstyle.c
+++ b/Sources/backends/cstyle.c
@@ -89,10 +89,15 @@ void cstyle_write_opcode(char *code, size_t *offset, opcode *o, type_string_func
 			break;
 		}
 		break;
-	case OPCODE_LOAD_CONSTANT:
+	case OPCODE_LOAD_FLOAT_CONSTANT:
 		indent(code, offset, *indentation);
-		*offset += sprintf(&code[*offset], "%s _%" PRIu64 " = %f;\n", type_string(o->op_load_constant.to.type.type), o->op_load_constant.to.index,
-		                   o->op_load_constant.number);
+		*offset += sprintf(&code[*offset], "%s _%" PRIu64 " = %f;\n", type_string(o->op_load_float_constant.to.type.type), o->op_load_float_constant.to.index,
+		                   o->op_load_float_constant.number);
+		break;
+	case OPCODE_LOAD_BOOL_CONSTANT:
+		indent(code, offset, *indentation);
+		*offset += sprintf(&code[*offset], "%s _%" PRIu64 " = %s;\n", type_string(o->op_load_bool_constant.to.type.type), o->op_load_bool_constant.to.index,
+		                   o->op_load_bool_constant.boolean ? "true" : "false");
 		break;
 	case OPCODE_ADD: {
 		indent(code, offset, *indentation);
diff --git a/Sources/backends/hlsl.c b/Sources/backends/hlsl.c
index 668517b..838d23d 100644
--- a/Sources/backends/hlsl.c
+++ b/Sources/backends/hlsl.c
@@ -603,6 +603,10 @@ static void write_functions(char *hlsl, size_t *offset, shader_stage stage, func
 					check(o->op_call.parameters_size == 0, context, "group_index can not have a parameter");
 					*offset += sprintf(&hlsl[*offset], "%s _%" PRIu64 " = SV_GroupIndex;\n", type_string(o->op_call.var.type.type), o->op_call.var.index);
 				}
+				else if (o->op_call.func == add_name("world_ray_direction")) {
+					check(o->op_call.parameters_size == 0, context, "group_index can not have a parameter");
+					*offset += sprintf(&hlsl[*offset], "%s _%" PRIu64 " = WorldRayDirection();\n", type_string(o->op_call.var.type.type), o->op_call.var.index);
+				}
 				else {
 					*offset += sprintf(&hlsl[*offset], "%s _%" PRIu64 " = %s(", type_string(o->op_call.var.type.type), o->op_call.var.index,
 					                   function_string(o->op_call.func));
diff --git a/Sources/backends/spirv.c b/Sources/backends/spirv.c
index 4a40f6c..058b7ca 100644
--- a/Sources/backends/spirv.c
+++ b/Sources/backends/spirv.c
@@ -533,6 +533,11 @@ static spirv_id write_constant_float(instructions_buffer *instructions, spirv_id
 	return write_constant(instructions, spirv_float_type, value_id, uint32_value);
 }
 
+static spirv_id write_constant_bool(instructions_buffer *instructions, spirv_id value_id, bool value) {
+	uint32_t uint32_value = *(uint32_t *)&value;
+	return write_constant(instructions, spirv_bool_type, value_id, uint32_value);
+}
+
 static void write_vertex_output_decorations(instructions_buffer *instructions, spirv_id output_struct) {
 	{
 		uint32_t operands[] = {output_struct.id, 0, (uint32_t)DECORATION_BUILTIN, (uint32_t)BUILTIN_POSITION};
@@ -629,6 +634,20 @@ static spirv_id get_float_constant(float value) {
 	return index;
 }
 
+static struct {
+	bool key;
+	spirv_id value;
+} *bool_constants = NULL;
+
+static spirv_id get_bool_constant(bool value) {
+	spirv_id index = hmget(bool_constants, value);
+	if (index.id == 0) {
+		index = allocate_index();
+		hmput(bool_constants, value, index);
+	}
+	return index;
+}
+
 static spirv_id write_op_access_chain(instructions_buffer *instructions, spirv_id result_type, spirv_id base, int *indices, uint16_t indices_size) {
 	spirv_id pointer = allocate_index();
 
@@ -819,9 +838,14 @@ static void write_function(instructions_buffer *instructions, function *f, spirv
 
 			break;
 		}
-		case OPCODE_LOAD_CONSTANT: {
-			spirv_id id = get_float_constant(o->op_load_constant.number);
-			hmput(index_map, o->op_load_constant.to.index, id);
+		case OPCODE_LOAD_FLOAT_CONSTANT: {
+			spirv_id id = get_float_constant(o->op_load_float_constant.number);
+			hmput(index_map, o->op_load_float_constant.to.index, id);
+			break;
+		}
+		case OPCODE_LOAD_BOOL_CONSTANT: {
+			spirv_id id = get_bool_constant(o->op_load_bool_constant.boolean);
+			hmput(index_map, o->op_load_bool_constant.to.index, id);
 			break;
 		}
 		case OPCODE_CALL: {
@@ -1046,6 +1070,11 @@ static void write_constants(instructions_buffer *instructions) {
 	for (size_t i = 0; i < size; ++i) {
 		write_constant_float(instructions, float_constants[i].value, float_constants[i].key);
 	}
+
+	size = hmlenu(bool_constants);
+	for (size_t i = 0; i < size; ++i) {
+		write_constant_bool(instructions, bool_constants[i].value, bool_constants[i].key);
+	}
 }
 
 static void init_index_map(void) {
diff --git a/Sources/backends/wgsl.c b/Sources/backends/wgsl.c
index 38c5a28..3acbb37 100644
--- a/Sources/backends/wgsl.c
+++ b/Sources/backends/wgsl.c
@@ -566,10 +566,15 @@ static void write_functions(char *code, size_t *offset) {
 				                   type_string(o->op_binary.result.type.type), o->op_binary.left.index, o->op_binary.right.index);
 				break;
 			}
-			case OPCODE_LOAD_CONSTANT:
+			case OPCODE_LOAD_FLOAT_CONSTANT:
 				indent(code, offset, indentation);
-				*offset += sprintf(&code[*offset], "var _%" PRIu64 ": %s = %f;\n", o->op_load_constant.to.index,
-				                   type_string(o->op_load_constant.to.type.type), o->op_load_constant.number);
+				*offset += sprintf(&code[*offset], "var _%" PRIu64 ": %s = %f;\n", o->op_load_float_constant.to.index,
+				                   type_string(o->op_load_float_constant.to.type.type), o->op_load_float_constant.number);
+				break;
+			case OPCODE_LOAD_BOOL_CONSTANT:
+				indent(code, offset, indentation);
+				*offset += sprintf(&code[*offset], "var _%" PRIu64 ": %s = %s;\n", o->op_load_bool_constant.to.index,
+				                   type_string(o->op_load_bool_constant.to.type.type), o->op_load_bool_constant.boolean ? "true" : "false");
 				break;
 			case OPCODE_CALL: {
 				debug_context context = {0};
diff --git a/Sources/compiler.c b/Sources/compiler.c
index 8ac4817..34f4d87 100644
--- a/Sources/compiler.c
+++ b/Sources/compiler.c
@@ -399,8 +399,19 @@ variable emit_expression(opcodes *code, block *parent, expression *e) {
 		}
 	}
 	case EXPRESSION_BOOLEAN: {
-		debug_context context = {0};
-		error(context, "not implemented");
+		type_ref t;
+		init_type_ref(&t, NO_NAME);
+		t.type = float_id;
+		variable v = allocate_variable(t, VARIABLE_LOCAL);
+
+		opcode o;
+		o.type = OPCODE_LOAD_BOOL_CONSTANT;
+		o.size = OP_SIZE(o, op_load_bool_constant);
+		o.op_load_bool_constant.boolean = e->boolean;
+		o.op_load_bool_constant.to = v;
+		emit_op(code, &o);
+
+		return v;
 	}
 	case EXPRESSION_NUMBER: {
 		type_ref t;
@@ -409,10 +420,10 @@ variable emit_expression(opcodes *code, block *parent, expression *e) {
 		variable v = allocate_variable(t, VARIABLE_LOCAL);
 
 		opcode o;
-		o.type = OPCODE_LOAD_CONSTANT;
-		o.size = OP_SIZE(o, op_load_constant);
-		o.op_load_constant.number = (float)e->number;
-		o.op_load_constant.to = v;
+		o.type = OPCODE_LOAD_FLOAT_CONSTANT;
+		o.size = OP_SIZE(o, op_load_float_constant);
+		o.op_load_float_constant.number = (float)e->number;
+		o.op_load_float_constant.to = v;
 		emit_op(code, &o);
 
 		return v;
@@ -457,9 +468,12 @@ variable emit_expression(opcodes *code, block *parent, expression *e) {
 		o.size = OP_SIZE(o, op_load_member);
 
 		debug_context context = {0};
-		check(e->member.left->kind == EXPRESSION_VARIABLE, context, "Misformed member construct");
-
-		o.op_load_member.from = find_variable(parent, e->member.left->variable);
+		if (e->member.left->kind == EXPRESSION_VARIABLE) {
+			o.op_load_member.from = find_variable(parent, e->member.left->variable);
+		}
+		else {
+			o.op_load_member.from = emit_expression(code, parent, e->member.left);
+		}
 
 		check(o.op_load_member.from.index != 0, context, "Load var is broken");
 		o.op_load_member.to = v;
diff --git a/Sources/compiler.h b/Sources/compiler.h
index b46bebd..ea274de 100644
--- a/Sources/compiler.h
+++ b/Sources/compiler.h
@@ -28,7 +28,8 @@ typedef struct opcode {
 		OPCODE_ADD_AND_STORE_MEMBER,
 		OPCODE_DIVIDE_AND_STORE_MEMBER,
 		OPCODE_MULTIPLY_AND_STORE_MEMBER,
-		OPCODE_LOAD_CONSTANT,
+		OPCODE_LOAD_FLOAT_CONSTANT,
+		OPCODE_LOAD_BOOL_CONSTANT,
 		OPCODE_LOAD_MEMBER,
 		OPCODE_RETURN,
 		OPCODE_CALL,
@@ -77,7 +78,11 @@ typedef struct opcode {
 		struct {
 			float number;
 			variable to;
-		} op_load_constant;
+		} op_load_float_constant;
+		struct {
+			bool boolean;
+			variable to;
+		} op_load_bool_constant;
 		struct {
 			variable from;
 			variable to;
diff --git a/Sources/functions.c b/Sources/functions.c
index 3a98209..1e70a9c 100644
--- a/Sources/functions.c
+++ b/Sources/functions.c
@@ -49,6 +49,18 @@ static void add_func_float3(char *name) {
 	f->block = NULL;
 }
 
+static void add_func_float_float(char *name) {
+	function_id func = add_function(add_name(name));
+	function *f = get_function(func);
+	init_type_ref(&f->return_type, add_name("float"));
+	f->return_type.type = find_type_by_ref(&f->return_type);
+	f->parameter_names[0] = add_name("a");
+	init_type_ref(&f->parameter_types[0], add_name("float"));
+	f->parameter_types[0].type = find_type_by_ref(&f->parameter_types[0]);
+	f->parameters_size = 1;
+	f->block = NULL;
+}
+
 static void add_func_float3_float3(char *name) {
 	function_id func = add_function(add_name(name));
 	function *f = get_function(func);
@@ -136,6 +148,7 @@ void functions_init(void) {
 	add_func_float3_float_float_float("lerp");
 	add_func_float3("world_ray_direction");
 	add_func_float3_float3("normalize");
+	add_func_float_float("saturate");
 }
 
 static void grow_if_needed(uint64_t size) {
diff --git a/tests/in/test.kong b/tests/in/test.kong
index ed28b65..ec36b72 100644
--- a/tests/in/test.kong
+++ b/tests/in/test.kong
@@ -1,4 +1,4 @@
-struct VertexIn {
+/*struct VertexIn {
     position: float3;
 }
 
@@ -19,10 +19,14 @@ fun pos(input: VertexIn): FragmentIn {
 #[fragment]
 fun pixel(input: FragmentIn): float4 {
     var color: float4;
-    color.r = 1.0;
-    color.g = 0.0;
+    color.r = 0.0;
+    color.g = 1.0;
     color.b = 0.0;
     color.a = 1.0;
+
+    var a: int = 3;
+    a += 2;
+
     return color;
 }
 
@@ -32,4 +36,43 @@ struct Pipe {
     fragment = pixel;
 }
 
-// https://shader-playground.timjones.io/9daab221a76e1fe6fe3fbad510c436c1
+#[compute, threads(32, 1, 1)]
+fun comp(): void {
+
+}*/
+
+// based on https://landelare.github.io/2023/02/18/dxr-tutorial.html
+
+const camera: float3 = float3(0, 1.5, -7);
+const light: float3 = float3(0, 200, 0);
+const skyTop: float3 = float3(0.24, 0.44, 0.72);
+const skyBottom: float3 = float3(0.75, 0.86, 0.93);
+
+struct Payload {
+    color: float3;
+    allow_reflection: bool;
+    missed: bool;
+}
+
+fun sendrays(): void {
+
+}
+
+fun raymissed(payload: Payload): void {
+    var slope: float = normalize(world_ray_direction()).y;
+    var t: float = saturate(slope * 5 + 0.5);
+    payload.color = lerp(skyBottom, skyTop, t);
+
+    payload.missed = true;
+}
+
+fun closesthit(payload: Payload, uv: float2): void {
+
+}
+
+#[raypipe]
+struct RayPipe {
+    gen = sendrays;
+    miss = raymissed;
+    closest = closesthit;
+}