diff --git a/ui-shaders.cc b/ui-shaders.cc
index 641763f..5cd3958 100644
--- a/ui-shaders.cc
+++ b/ui-shaders.cc
@@ -605,6 +605,19 @@ T_ShaderProgram T_ShaderManager::program(
 	return T_ShaderProgram( *programIndex_.get( name ) + 1 );
 }
 
+T_ShaderProgram T_ShaderManager::program(
+		T_String const& name ,
+		const E_ShaderType type ,
+		char const* const source )
+{
+	assert( name && name.startsWith( "*" ) );
+	assert( type != E_ShaderType::__COUNT__ );
+	loadBuiltinProgram( name , type , source );
+	assert( programIndex_.contains( name ) );
+	assert( programs_[ *programIndex_.get( name ) ].name == name );
+	return T_ShaderProgram( *programIndex_.get( name ) + 1 );
+}
+
 T_ShaderPipeline T_ShaderManager::pipeline(
 		std::initializer_list< T_String > shaders )
 {
@@ -753,6 +766,7 @@ void T_ShaderManager::update( )
 	updates_.clear( );
 }
 
+/*----------------------------------------------------------------------------*/
 
 void T_ShaderManager::loadProgram(
 		T_String const& pipeline ,
@@ -785,6 +799,16 @@ void T_ShaderManager::loadProgram(
 	}
 }
 
+void T_ShaderManager::loadBuiltinProgram(
+		T_String const& name ,
+		const E_ShaderType type ,
+		char const* const source )
+{
+	if ( !useExistingProgram( name ) ) {
+		initProgramRecord( name , type , source );
+	}
+}
+
 bool T_ShaderManager::useExistingProgram(
 		T_String const& name )
 {
@@ -796,6 +820,7 @@ bool T_ShaderManager::useExistingProgram(
 	return true;
 }
 
+/*----------------------------------------------------------------------------*/
 
 uint32_t T_ShaderManager::newProgramRecord( )
 {
@@ -812,14 +837,20 @@ uint32_t T_ShaderManager::newProgramRecord( )
 }
 
 T_ShaderManager::T_Program_& T_ShaderManager::initProgramRecord(
-		T_String const& name )
+		T_String const& name ,
+		const E_ShaderType type ,
+		char const* const source )
 {
 	const uint32_t index( newProgramRecord( ) );
 	auto& program( programs_[ index ] );
 	programIndex_.add( name , index );
 	program.name = name;
 	program.id = 0;
-	initProgram( program );
+	if ( type == E_ShaderType::__COUNT__ || source == nullptr ) {
+		initProgram( program );
+	} else {
+		initBuiltinProgram( program , type , source );
+	}
 
 	T_StringBuilder sb;
 	for ( auto const& e : program.code.errors ) {
@@ -829,9 +860,14 @@ T_ShaderManager::T_Program_& T_ShaderManager::initProgramRecord(
 	sb << '\0';
 	printf( "%s" , sb.data( ) );
 
+	if ( type != E_ShaderType::__COUNT__ && source != nullptr && program.id == 0 ) {
+		std::abort( );
+	}
+
 	return program;
 }
 
+/*----------------------------------------------------------------------------*/
 
 T_ShaderInput const* T_ShaderManager::getInput(
 		T_String const& name )
@@ -851,6 +887,7 @@ T_ShaderInput const* T_ShaderManager::getInput(
 	return inputs_.get( name )->get( );
 }
 
+/*----------------------------------------------------------------------------*/
 
 void T_ShaderManager::dereferencePipeline(
 		T_String const& id )
@@ -876,7 +913,6 @@ void T_ShaderManager::dereferencePipeline(
 	pipelines_.remove( pipeline.idString );
 }
 
-
 void T_ShaderManager::dereferenceProgram(
 		const uint32_t index ,
 		T_String const& pipeline )
@@ -893,7 +929,6 @@ void T_ShaderManager::dereferenceProgram(
 	programIndex_.remove( program.name );
 }
 
-
 void T_ShaderManager::dereferenceProgram(
 		const uint32_t index )
 {
@@ -907,6 +942,7 @@ void T_ShaderManager::dereferenceProgram(
 	programIndex_.remove( program.name );
 }
 
+/*----------------------------------------------------------------------------*/
 
 void T_ShaderManager::initPipeline(
 		T_Pipeline_& pipeline ) const
@@ -996,8 +1032,33 @@ void T_ShaderManager::initProgram(
 	if ( !( built && code.errors.empty( ) ) ) {
 		return;
 	}
+	buildProgram( program );
+}
 
+void T_ShaderManager::initBuiltinProgram(
+		T_Program_& program ,
+		E_ShaderType type ,
+		char const* source )
+{
+	auto name( program.name );
+	printf( "init built-in program %s\n" ,
+			program.name.substr( 1 ).toOSString( ).data( ) );
+
+	program.code.code << source << '\0';
+	program.code.type = type;
+	program.code.counts.add( UINT32_MAX );
+	program.code.starts.add( 0 );
+	program.code.sources.add( program.name );
+
+	buildProgram( program );
+}
+
+void T_ShaderManager::buildProgram(
+		T_Program_& program )
+{
 	// Try to compile the program
+	auto& code( program.code );
+	auto const& name( program.name );
 	char const* const list[] = {
 		program.code.code.data( )
 	};
@@ -1039,6 +1100,8 @@ void T_ShaderManager::initProgram(
 	}
 }
 
+/*----------------------------------------------------------------------------*/
+
 void T_ShaderManager::parseGLSLError(
 		T_ShaderCode& code ,
 		char const* errorLine )
@@ -1065,6 +1128,8 @@ void T_ShaderManager::parseGLSLError(
 			errorLine );
 }
 
+/*----------------------------------------------------------------------------*/
+
 void T_ShaderManager::programUpdated(
 		T_String const& name )
 {
@@ -1120,15 +1185,17 @@ void T_ShaderManager::makeUI( )
 	Begin( "Shaders" , &uiEnabled_ , ImGuiWindowFlags_NoCollapse );
 
 	const auto n( std::count_if( programs_.begin( ) , programs_.end( ) ,
-			[]( auto const& p ) {
-				return !p.plReferences.empty( ) || p.saReferences != 0;
-			} ) );
+		[]( auto const& p ) {
+			return ( !p.plReferences.empty( ) || p.saReferences != 0 )
+				&& !p.name.startsWith( "*" );
+		} ) );
 
 	std::vector< size_t > indices;
 	const auto rn = programs_.size( );
 	for ( auto i = 0u ; i < rn ; i ++ ) {
 		auto const& p( programs_[ i ] );
-		if ( !p.plReferences.empty( ) || p.saReferences ) {
+		if ( ( !p.plReferences.empty( ) || p.saReferences )
+				&& !p.name.startsWith( "*" ) ) {
 			indices.push_back( i );
 		}
 	}
diff --git a/ui-shaders.hh b/ui-shaders.hh
index 4f5e5d1..571a30e 100644
--- a/ui-shaders.hh
+++ b/ui-shaders.hh
@@ -156,7 +156,13 @@ struct T_ShaderManager
 
 	T_ShaderManager( ) noexcept;
 
+	// Build / get a program based on its name
 	T_ShaderProgram program( T_String const& name );
+	// Build a program from its source code
+	T_ShaderProgram program( T_String const& name ,
+			E_ShaderType type ,
+			char const* source );
+
 	T_ShaderPipeline pipeline(
 			std::initializer_list< T_String > shaders );
 	T_ShaderPipeline pipeline(
@@ -225,11 +231,17 @@ struct T_ShaderManager
 
 	// Load/use existing program for standalone use
 	void loadProgram( T_String const& name );
+	void loadBuiltinProgram(
+			T_String const& name ,
+			E_ShaderType type ,
+			char const* source );
 	bool useExistingProgram( T_String const& name );
 
 	// Program management
 	T_Program_& initProgramRecord(				// Init management data
-			T_String const& record );
+			T_String const& record ,
+			E_ShaderType type = E_ShaderType::__COUNT__ ,
+			char const* source = nullptr );
 	uint32_t newProgramRecord( );				// Allocate entry in index
 
 	T_ShaderInput const* getInput(
@@ -244,6 +256,10 @@ struct T_ShaderManager
 
 	void initPipeline( T_Pipeline_& pipeline ) const;
 	void initProgram( T_Program_& program );
+	void initBuiltinProgram( T_Program_& program ,
+			E_ShaderType type ,
+			char const* source );
+	void buildProgram( T_Program_& program );
 
 	void parseGLSLError(
 			T_ShaderCode& code ,