diff --git a/camera.hh b/camera.hh
index 0c85e89..eb1ae52 100644
--- a/camera.hh
+++ b/camera.hh
@@ -63,7 +63,7 @@ struct T_Camera
 			glm::vec3 const& angles ,
 			float distance ) noexcept;
 	void camera( glm::vec3 const& position ,
-			glm::vec3 const& directionOrTarget ,
+			glm::vec3 const& target ,
 			glm::vec3 const& up ) noexcept;
 
     private:
diff --git a/demo.srd b/demo.srd
index 07c7682..358f820 100644
--- a/demo.srd
+++ b/demo.srd
@@ -86,6 +86,11 @@
 				(int "Correction steps" raymarcher-correction
 					(min 0) (max 100) (slider))
 			)
+			(camera "Camera"
+				(near-plane camera-nearplane)
+				(position camera-pos-x camera-pos-y camera-pos-z)
+				(up camera-up-x camera-up-y camera-up-z)
+				(look-at camera-lookat-x camera-lookat-y camera-lookat-z))
 			(float "Fog" fog
 				(min 0) (max 1) (step .000005) (decimals 5))
 		)
diff --git a/syncoverrides.cc b/syncoverrides.cc
index 6d301b6..6223754 100644
--- a/syncoverrides.cc
+++ b/syncoverrides.cc
@@ -328,6 +328,135 @@ bool CgSetUnit_( T_SRDParserData const& data )
 	return true;
 }
 
+/*------------------------------------------------------------------------------*/
+
+using SP_Cam = T_SharedPtr< T_CamOverride >;
+
+bool EnterCam_( T_SRDParserData const& data )
+{
+	auto const& input( *( data.input ) );
+	SP_Cam ptr{ NewShared< T_CamOverride >( input[ 1 ].stringValue( ) ) };
+	ptr->location( ) = input[ 0 ].location( );
+	*( data.targetData ) = std::move( ptr );
+	return true;
+}
+
+bool ExitCam_( T_SRDParserData const& data )
+{
+	auto const& input( *( data.input ) );
+	auto& ov( data.currentData->value< SP_Cam >( ) );
+	auto& parent( data.targetData->value< SP_Section >( ) );
+
+	if ( !ov->isFovConfigured( ) ) {
+		data.errors.add( "field of view or near plane missing" , input[ 0 ] );
+	}
+	if ( !ov->isTargetConfigured( ) ) {
+		data.errors.add( "target vector missing" , input[ 0 ] );
+	}
+	if ( !ov->checkValidConfig( ) ) {
+		data.errors.add( "invalid camera configuration" , input[ 0 ] );
+	}
+
+	parent->overrides.add( ov.makeOwned( ) );
+	return true;
+}
+
+bool CamSetFov_( T_SRDParserData const& data )
+{
+	auto const& input( *( data.input ) );
+	auto& ov( data.currentData->value< SP_Cam >( ) );
+	const auto s{ ov->setFieldOfView( input[ 1 ].stringValue( ) ) };
+	if ( s == T_CamOverride::S_DEF ) {
+		data.errors.add( "field of view or near plane already set" , input[ 0 ] );
+	} else if ( s == T_CamOverride::S_INPUTS ) {
+		data.errors.add( "input already in use" , input[ 1 ] );
+	}
+	return true;
+}
+
+bool CamSetNP_( T_SRDParserData const& data )
+{
+	auto const& input( *( data.input ) );
+	auto& ov( data.currentData->value< SP_Cam >( ) );
+	const auto s{ ov->setNearPlane( input[ 1 ].stringValue( ) ) };
+	if ( s == T_CamOverride::S_DEF ) {
+		data.errors.add( "field of view or near plane already set" , input[ 0 ] );
+	} else if ( s == T_CamOverride::S_INPUTS ) {
+		data.errors.add( "input already in use" , input[ 1 ] );
+	}
+	return true;
+}
+
+bool CamSetTarget_( T_SRDParserData const& data )
+{
+	auto const& input( *( data.input ) );
+	auto& ov( data.currentData->value< SP_Cam >( ) );
+	const auto s{ ov->setTarget( input[ 1 ].stringValue( ) ,
+			input[ 2 ].stringValue( ) ,
+			input[ 3 ].stringValue( ) ) };
+	if ( s == T_CamOverride::S_DEF ) {
+		data.errors.add( "camera target already set" , input[ 0 ] );
+	} else if ( s == T_CamOverride::S_INPUTS ) {
+		data.errors.add( "inputs already in use" , input[ 1 ] );
+	}
+	return true;
+}
+
+bool CamSetPosition_( T_SRDParserData const& data )
+{
+	auto const& input( *( data.input ) );
+	auto& ov( data.currentData->value< SP_Cam >( ) );
+	const auto s{ ov->setPositionVector( input[ 1 ].stringValue( ) ,
+			input[ 2 ].stringValue( ) , input[ 3 ].stringValue( ) ) };
+	if ( s == T_CamOverride::S_DEF ) {
+		data.errors.add( "camera position already set" , input[ 0 ] );
+	} else if ( s == T_CamOverride::S_INPUTS ) {
+		data.errors.add( "inputs already in use" , input[ 1 ] );
+	}
+	return true;
+}
+
+bool CamSetUpVector_( T_SRDParserData const& data )
+{
+	auto const& input( *( data.input ) );
+	auto& ov( data.currentData->value< SP_Cam >( ) );
+	const auto s{ ov->setUpVector( input[ 1 ].stringValue( ) ,
+			input[ 2 ].stringValue( ) , input[ 3 ].stringValue( ) ) };
+	if ( s == T_CamOverride::S_DEF ) {
+		data.errors.add( "'up' vector already set" , input[ 0 ] );
+	} else if ( s == T_CamOverride::S_INPUTS ) {
+		data.errors.add( "inputs already in use" , input[ 1 ] );
+	}
+	return true;
+}
+
+bool CamSetAngles_( T_SRDParserData const& data )
+{
+	auto const& input( *( data.input ) );
+	auto& ov( data.currentData->value< SP_Cam >( ) );
+	const auto s{ ov->setAngles( input[ 1 ].stringValue( ) ,
+			input[ 2 ].stringValue( ) , input[ 3 ].stringValue( ) ) };
+	if ( s == T_CamOverride::S_DEF ) {
+		data.errors.add( "camera angles already set" , input[ 0 ] );
+	} else if ( s == T_CamOverride::S_INPUTS ) {
+		data.errors.add( "inputs already in use" , input[ 1 ] );
+	}
+	return true;
+}
+
+bool CamSetDistance_( T_SRDParserData const& data )
+{
+	auto const& input( *( data.input ) );
+	auto& ov( data.currentData->value< SP_Cam >( ) );
+	const auto s{ ov->setDistance( input[ 1 ].stringValue( ) ) };
+	if ( s == T_CamOverride::S_DEF ) {
+		data.errors.add( "camera distance already set" , input[ 0 ] );
+	} else if ( s == T_CamOverride::S_INPUTS ) {
+		data.errors.add( "input already in use" , input[ 1 ] );
+	}
+	return true;
+}
+
 
 } // namespace
 /*------------------------------------------------------------------------------*/
@@ -398,6 +527,10 @@ ebcl::T_SRDParserConfig sov::GetParserConfig( )
 				<< EnterContext( "color-grading" )
 				<< OnEnter( EnterColorGrading_ )
 				<< OnExit( AddColorGrading_ ) )
+		// Camera controls
+		<< ( Rule() << "camera" << Text( ) << EnterContext( "camera" )
+				<< OnEnter( EnterCam_ )
+				<< OnExit( ExitCam_ ) )
 	;
 
 	// Floating point control parameters
@@ -424,6 +557,22 @@ ebcl::T_SRDParserConfig sov::GetParserConfig( )
 		<< ( Rule() << "unit" << Numeric( ) << CgSetUnit_ )
 	;
 
+	// Camera controls
+	defs.context( "camera" )
+		<< ( Rule() << "fov" << Word() << CamSetFov_ )
+		<< ( Rule() << "near-plane" << Word() << CamSetNP_ )
+		//
+		<< ( Rule() << ( Alt() << "target" << "look-at" )
+				<< ( SRD::Times( 3 ) << Word() )
+				<< CamSetTarget_ )
+		//
+		<< ( Rule() << "position" << ( SRD::Times( 3 ) << Word() ) << CamSetPosition_ )
+		<< ( Rule() << "up" << ( SRD::Times( 3 ) << Word() ) << CamSetUpVector_ )
+		//
+		<< ( Rule() << "angles" << ( SRD::Times( 3 ) << Word() ) << CamSetAngles_ )
+		<< ( Rule() << "distance" << Word() << CamSetDistance_ )
+	;
+
 	return T_SRDParserConfig{ defs };
 }
 
@@ -838,3 +987,142 @@ bool T_ColorGrading::setUnit(
 	assert( v != 0 );
 	M_SETOPT_( unit_ , v );
 }
+
+
+/*= T_CamOverride ==============================================================*/
+
+T_CamOverride::T_CamOverride(
+		T_String const& title ) noexcept
+	: A_SyncOverride( "cam" , title ) ,
+		camMode_( CM_INVALID )
+{}
+
+/*------------------------------------------------------------------------------*/
+
+T_CamOverride::E_SetState T_CamOverride::setFieldOfView(
+		T_String const& input ) noexcept
+{
+	if ( fovConfig_ ) {
+		return S_DEF;
+	}
+	if ( inputs_.contains( input ) ) {
+		return S_INPUTS;
+	}
+	fovConfig_.setNew( FM_FOV , inputs_.size( ) );
+	inputs_.add( input );
+	return S_OK;
+}
+
+T_CamOverride::E_SetState T_CamOverride::setNearPlane(
+		T_String const& input ) noexcept
+{
+	if ( fovConfig_ ) {
+		return S_DEF;
+	}
+	if ( inputs_.contains( input ) ) {
+		return S_INPUTS;
+	}
+	fovConfig_.setNew( FM_NEARPLANE , inputs_.size( ) );
+	inputs_.add( input );
+	return S_OK;
+}
+
+/*------------------------------------------------------------------------------*/
+
+T_CamOverride::E_SetState T_CamOverride::setTarget(
+		T_String const& inX ,
+		T_String const& inY ,
+		T_String const& inZ ) noexcept
+{
+	return setVector( target_ , inX , inY , inZ );
+}
+
+/*------------------------------------------------------------------------------*/
+
+T_CamOverride::E_SetState T_CamOverride::setUpVector(
+		T_String const& inX ,
+		T_String const& inY ,
+		T_String const& inZ ) noexcept
+{
+	return setVector( upVector_ , inX , inY , inZ );
+}
+
+T_CamOverride::E_SetState T_CamOverride::setPositionVector(
+		T_String const& inX ,
+		T_String const& inY ,
+		T_String const& inZ ) noexcept
+{
+	return setVector( position_ , inX , inY , inZ );
+}
+
+/*------------------------------------------------------------------------------*/
+
+T_CamOverride::E_SetState T_CamOverride::setAngles(
+		T_String const& in1 ,
+		T_String const& in2 ,
+		T_String const& in3 ) noexcept
+{
+	return setVector( angles_ , in1 , in2 , in3 );
+}
+
+T_CamOverride::E_SetState T_CamOverride::setDistance(
+		T_String const& input ) noexcept
+{
+	if ( distance_ ) {
+		return S_DEF;
+	}
+	if ( inputs_.contains( input ) ) {
+		return S_INPUTS;
+	}
+	distance_ = inputs_.size( );
+	inputs_.add( input );
+	return S_OK;
+}
+
+/*------------------------------------------------------------------------------*/
+
+bool T_CamOverride::checkValidConfig( ) noexcept
+{
+	if ( camMode_ != CM_INVALID ) {
+		return true;
+	}
+
+	if ( angles_ && distance_ && !( position_ || upVector_ ) ) {
+		camMode_ = CM_ANGLES;
+	} else if ( position_ && upVector_ && !( angles_ || distance_ ) ) {
+		camMode_ = CM_VECTORS;
+	}
+	return ( camMode_ != CM_INVALID );
+}
+
+/*------------------------------------------------------------------------------*/
+
+T_CamOverride::E_SetState T_CamOverride::setVector(
+		T_Optional< T_VectorConfig_ >& vector ,
+		T_String const& inX ,
+		T_String const& inY ,
+		T_String const& inZ ) noexcept
+{
+	if ( vector ) {
+		return S_DEF;
+	}
+	if ( inputs_.contains( inX ) || inputs_.contains( inY ) || inputs_.contains( inZ ) ) {
+		return S_INPUTS;
+	}
+
+	const uint32_t s( inputs_.size( ) );
+	vector.setNew( s , s+1 , s+2 );
+	inputs_.add( inX );
+	inputs_.add( inY );
+	inputs_.add( inZ );
+	return S_OK;
+}
+
+/*------------------------------------------------------------------------------*/
+
+void T_CamOverride::makeEditWidgets(
+		uint32_t& counter ,
+		T_StringBuilder& sb ) noexcept
+{
+#warning implement the fuck
+}
diff --git a/syncoverrides.hh b/syncoverrides.hh
index da23bf6..6dc3897 100644
--- a/syncoverrides.hh
+++ b/syncoverrides.hh
@@ -1,5 +1,6 @@
 #pragma once
 #include "sync.hh"
+#include "camera.hh"
 
 namespace sov {
 
@@ -211,6 +212,115 @@ class T_ColorGrading : public A_SyncOverride
 };
 
 
+/*= CAMERA CONTROLS ============================================================*/
+
+class T_CamOverride : public A_SyncOverride
+{
+    public:
+	enum E_SetState {
+		S_OK ,		// Inputs were set
+		S_DEF ,		// Duplicate definition
+		S_INPUTS ,	// Duplicate inputs
+	};
+
+    private:
+	enum E_CamMode_ {
+		CM_INVALID ,
+		CM_ANGLES ,
+		CM_VECTORS
+	};
+
+	enum E_FovMode_ {
+		FM_INVALID ,
+		FM_FOV ,
+		FM_NEARPLANE ,
+	};
+
+	struct T_FovConfig_
+	{
+		E_FovMode_ mode;
+		uint32_t inputIndex;
+
+		T_FovConfig_( E_FovMode_ mode , uint32_t idx ) noexcept
+			: mode( mode ) , inputIndex( idx )
+		{ assert( mode != FM_INVALID ); }
+	};
+
+	struct T_VectorConfig_
+	{
+		uint32_t x , y , z;
+
+		T_VectorConfig_( uint32_t x , uint32_t y , uint32_t z ) noexcept
+			: x(x),y(y),z(z)
+		{}
+	};
+
+	T_Optional< T_FovConfig_ > fovConfig_;
+	T_Optional< T_VectorConfig_ > target_;
+
+	T_Optional< T_VectorConfig_ > upVector_;
+	T_Optional< T_VectorConfig_ > position_;
+
+	T_Optional< T_VectorConfig_ > angles_;
+	T_Optional< uint32_t > distance_;
+
+	E_CamMode_ camMode_;
+	T_Camera camera_;
+
+    public:
+	T_CamOverride( T_String const& title ) noexcept;
+
+	E_SetState setFieldOfView(
+			T_String const& input ) noexcept;
+	E_SetState setNearPlane(
+			T_String const& input ) noexcept;
+
+	E_SetState setTarget(
+			T_String const& inX ,
+			T_String const& inY ,
+			T_String const& inZ ) noexcept;
+
+	E_SetState setUpVector(
+			T_String const& inX ,
+			T_String const& inY ,
+			T_String const& inZ ) noexcept;
+	E_SetState setPositionVector(
+			T_String const& inX ,
+			T_String const& inY ,
+			T_String const& inZ ) noexcept;
+
+	E_SetState setAngles(
+			T_String const& in1 ,
+			T_String const& in2 ,
+			T_String const& in3 ) noexcept;
+	E_SetState setDistance(
+			T_String const& input ) noexcept;
+
+	bool isFovConfigured( ) const noexcept
+		{ return fovConfig_; }
+	bool isTargetConfigured( ) const noexcept
+		{ return target_; }
+	bool checkValidConfig( ) noexcept;
+
+	T_Camera& camData( ) noexcept
+		{ return camera_; }
+	T_Camera const& camData( ) const noexcept
+		{ return camera_; }
+
+    private:
+	E_SetState setVector(
+			T_Optional< T_VectorConfig_ >& vector ,
+			T_String const& inX ,
+			T_String const& inY ,
+			T_String const& inZ ) noexcept;
+
+    protected:
+	void makeEditWidgets(
+			uint32_t& counter ,
+			T_StringBuilder& sb ) noexcept override;
+};
+
+
 /*= PARSER CONFIGURATION =======================================================*/
 
 // Get a parser configuration that will be able to parse UI override definitions.