test/BloodSplat.cs
public sealed class BloodSplat : Component, Component.ICollisionListener
{
	[Property] Rigidbody Body { get; set; }
	[Property] GameObject Ball { get; set; }
	[Property] Color TintColor { get; set; } = Color.Red;

	protected override void OnStart()
	{
		var vec = Vector3.Random;
		vec = vec.WithZ( vec.z.Remap( -1, 1, 0.2f, 1 ) );
		Body.ApplyImpulse( (vec * 0.5f).WithZ( vec.z ) * 900f );
		Ball.LocalRotation = Rotation.Random;
		Body.AngularVelocity = Vector3.Random * 10f;
	}

	void ICollisionListener.OnCollisionStart( Collision collision )
	{
		if ( collision.Other.Collider.Tags.Has( "world" ) )
		{
			using ( Scene.Push() )
			{
				var go = new GameObject( "Splat" );
				go.WorldPosition = collision.Contact.Point;
				var comp = go.AddComponent<GaussianSplatVolume>();
				comp.Mode = SplatVolumeMode.Color;
				comp.Shape = SplatVolumeShape.Sphere;
				comp.Radius = 20f;
				comp.Intensity = 0.66f;
				comp.IncludeTags = new TagSet( ["world"] );
				comp.TintColor = TintColor;
				var grow = go.AddComponent<GrowToSize>();
				grow.Volume = comp;
				grow.TargetSize = Random.Shared.Float( 15f, 35f );
				grow.GrowthRate = 5f;
				grow.ShrinkSpeed = 0.1f;
				comp.Falloff = grow.TargetSize * 0.2f; // Random.Shared.Float( 0.3f, 0.8f );
			}
			GameObject.Destroy();
		}
	}
}